Java温习——面向对象第二部分

    xiaoxiao2021-03-26  4

    一、深入变量

    1 变量的分类和初始值

    (1)分类

    根据在类中定义位置的不同,变量分为成员变量和局部变量;

    成员变量,又字段field(不要称为属性),直接定义在类中,各方法外,包括类成员变量(使用static修饰的字段)、实例成员变量;

    局部变量,除了成员变量,其他都是局部变量,包括方法内部的变量、方法的形参、代码块中的变量(一对花括号{}称为代码块);

    (2)初始值

    成员变量,默认有初始值,各数据类型的初始值如下

    局部变量,没有初始值,必须先初始化才能使用(初始化才会在内存中开辟空间);

    2 变量的作用域和生命周期

    (1)作用域

    作用域,指变量的存在范围,只有在该范围内程序代码才能访问它;

    当一个变量定义时,其作用域就确定了;变量根据定义的位置不同也决定了作用域不同(看变量所在的那对{});

    成员变量,在整个类中都有效,可先使用再定义(但不建议);

    局部变量,在定义的位置开始,到紧跟着结束的花括号为止,必须先定义再使用;

    (2)生命周期

    变量的作用域决定变量的生命周期,说明作用域不同,生命周期不同;

    生命周期,指一个变量被创建并分配内存空间开始,到该变量被销毁并清除所占内存空间的过程;

     存在位置生命周期开始生命周期结束在内存中的位置类变量使用static修饰的字段所在类的字节码加载到JVMJVM停止工作方法区实例变量没有使用static修饰的字段创建所在类的对象该对象被GC回收堆局部变量方法内部、方法形参或代码块初始化变量所在方法/代码块结束当前方法所在栈帧

    3 局部变量的初始化和在JVM中的运行机制

    局部变量定义后,必须显式初始化才能使用,因为系统不会为局部变量执行初始化操作,即定义局部变量后,系统并未为该变量分配内存空间;

    直到程序为该变量赋值时,系统才为该局部变量分配内存,并将初始值保存到该内存中;

    局部变量不属于任何类或实例,因为其总是保存在所在方法的栈内存中;

    基本数据类型的局部变量,直接把该变量的值保存到该变量所对应的内存中;

    引用数据类型的局部变量,内存存储地址,通过该地址值引用到该变量实际引用堆中对象;

    栈内存中变量无需系统垃圾回收,随方法或代码块的运行结束而结束;

    4 变量的定义和选择

    (1)定义

    <数据类型> <变量名>; <变量名> = <初始值>; // 或 <数据类型> <变量名> = <初始值>;

    (2)选择

    a 考虑因素

    考虑变量的生存时间,这会影响内存开销;

    扩大变量作用域,不利于提高程序的高内聚;

    b 使用成员变量和局部变量的情形

    实际开发中,尽量缩小变量的作用域,因此在内存中停留时间越短,性能越高;

    不要轻易使用static修饰,除了定义工具方法或变量属于类;

    不要轻易使用实例变量,因为存在线程不安全的问题;

    尽量使用局部变量;

    二、封装思想

    1 package语句

    (1)概念

    在开发中,存在成千上百的Java文件,若所有Java文件都在一个目录中,管理繁琐,可使用生活中的解决方案 —— 分类,但在Java中将文件夹称为包package;

    专门为当前Java文件设置包名;

    (2)语法格式

    package <包名>.<子包名>.<子子包名>; // 表示在<包名>文件夹下的<子包名>文件夹下的<子子包名>文件夹下

    必须将该语句作为Java文件中第一行代码(所有代码之前);

    (3)使用

    a 编译命令为 javac -d . <类名>.java

    若当前Java文件中没有使用package语句,表示在当前文件夹下生成字节码文件;

    若使用package语句,表示在当前文件夹下先生成包名,再在包中生成字节码文件;

    b 运行命令为java <包名>.<类名>

    (4)package最佳实践

    a 包名定义准则

    (a) 包名必须遵循标识符规范,且全部小写

    (b) 企业开发中,包名采用公司域名倒写,格式为package <域名倒写>.<模块名>.<组件名>;

    (如爱立信域名ericsson.com,则包名为com.ericsson.pss.util,用于进销存模块中专门存储工具类的包);

    注:在Android中,若package中使用了_,则无法部署到模拟器中,可使用一个字母代替_;

    (c) 自定义包名不能以java开头,因为Java的安全机制会检查

    b 类的名称

    类的简单名称 —— 定义类的名称

    类的全限定名称 —— 包名.类名

    如下,类的简单名称是PackageDemo,类的全限定名称是com.sissiy.hello.PackageDemo;

    package com.sissiy.hello; public class PackageDemo{ public static void main(String[] args){ System.our.println("Helo"); } }

    不同的包可以有相同的类名;

    c 在开发中,都是先有package,再在package中定义类;

    (5)JDK中的包名

    2 import语句

    当A类和B类不在同一个包中,若A类需要使用B类,此时A类必须引入B类;

    如数组的工具类Arrays类在java.util包中,Arrays类的全限定名为java.util.Arrays;

    没有使用import语句,操作不在同一个包中的类需要使用全限定名;

    class ImportDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7}; String ret = java.util.Arrays.toString(); System.out.println(ret); // 打印数组 } }

    使用import语句,直接把某个包中的类导入到当前类中,在该Java文件中可直接使用类的简单名称;

    语法格式

    import <全限定名>; // 只能导入一个类

    则上例更新为如下代码:

    import java.util.Arrays; class ImportDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7}; String ret = Arrays.toString(); System.out.println(ret); // 打印数组 } }

    注:

    编译器默认寻找java.lang包下的类,如String,因此不需导入,但不会导入java.lang的子包中的类,此时还需使用import语句;

    非java.lang的包都需要导入;

    若同时使用同一个包中的多个类,可使用*表示引入在当前文件中使用到的包中的所有类,语法格式如下:

    import <包名>.<子包名>.*;

    在Eclipse工具中,即使使用通配符*,在格式化代码时也会转换为N条import语句;

    3 static import语句

    如下代码,即使使用import语句,每次都要使用Arrays类名去调用静态方法;

    import java.util.Arrays; class StaticImportDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7}; String str = Arrays.toString(); System.out.println(str); Arrays.sort(arr); str = Arrays.toString(); System.out.println(str); } }

    把Arrays类中的静态成员作为当前Java文件的静态成员,可使用静态导入功能;

    语法格式

    import static <类的全限定名>.<该类中的static成员名>; import static <类的全限定名>.*; // *表示当前类的任意使用到的静态成员

    上例代码更新如下

    import java.util.Arrays; // 把java.util.Arrays类中的静态方法sort导入到StaticImportDemo类中 import static java.util.Arrays.sort; class StaticImportDemo{ public static void main(String[] args){ int[] arr = {1,2,3,4,5,6,7}; // 【注意】无法使用静态引入,因为Object类中也有toString方法,会造成指代不明错误,因此必须使用类名调用toString方法 String str = Arrays.toString(); System.out.println(str); sort(arr); str = Arrays.toString(); System.out.println(str); } }

    注:

    通过反编译工具,所谓的静态导入也是语法糖 —— 编译器的新特性;

    在实际开发中不适用静态导入,因为分不清某一个静态方法/字段来源于哪个类;

    Eclipse工具在格式化代码时取消所有静态导入,变成使用类名调用;

    4 封装

    (1)概念

    把对象的状态和行为看成一个统一的整体,将二者存放在一个独立的模块中(类);

    “信息隐藏” 把不需要让外界知道的信息隐藏起来,尽可能隐藏对象功能实现细节,向外暴露方法,保证外界安全访问功能;

    把所有字段使用private私有化,不准外界访问;把方法使用public修饰,允许外界访问;

    (2)好处

    使调用者正确方便地使用系统功能,防止调用者随意修改系统属性;

    提高组件的重用性;

    达到组件之间的低耦合性(当某个模块发生变化时,只要对外暴露的接口不变,便不影响其他模块);

    如使用封装思想设置一个人的年龄,代码实现如下

    class Person{ private String name; private int age; public void setAge(int age){ if(age < 0){ System.our.println("年龄不能为负数"); return; } this.age = age; } } public class PersonDemo{ public static void main(String[] args){ Person p = new Person(); p.setAge(18); } }

    5 访问权限修饰符

    访问权限修饰符针对Java中封装思想,规定在一个类中能看到什么,不能看到什么;

    访问权限控制有4种,访问权限修饰符有3种;

    (1)private —— 私有的,表示类访问权限,即只能在本类中访问,离开本类就不能直接访问;

    (2)缺省 —— 包私有,表示包访问权限,即访问者的包必须和当前定义类所在的包相同才可访问(子包也不行);

    (3)protected —— 子类访问权限,表示同包可以访问,即使不同包,只要有继承关系,也可以访问;

    (4)public —— 公共的,表示在当前项目中任何地方对其访问;

    注:

    一般,字段都使用private,为安全性而隐藏;

    一般,拥有实现细节的方法使用private,不希望外界(调用者)看到该方法的实现细节;方法使用public,供外界直接调用;

    // 直接暴露给外界,供调用者直接调用 public void doWork(){ methodA(); methodB(); methodC(); } // 仅仅完成部分操作,不需要调用者调用 private methodA(){} private methodB(){} private methodC(){}

    一般,不使用缺省,即使要使用,也只是暴露给同包中的其他类;

    一般,在继承关系中,父类需要把一个方法只暴露给子类,才使用protected;

    6 JavaBean规范

    (1)概念

    JavaBean是一种Java语言编写的可重用性组件(类);

    (2)必须遵循的规范

    类必须使用public修饰;

    必须保证有公共无参数构造器,即使手动提供带参数的构造器,也得提供无参数的构造器;

    包含属性的操作手段(给属性赋值,获取属性值);

    (3)分类

    复杂:UI,如Button、Panel、Window类

    简单:domain、dao、service组件,封装数据,操作数据库,逻辑运算等

    (封装有字段,并提供getter/setter)

    (4)成员

    方法Method

    事件event

    属性Property

    注:属性

    attribute:表示状态,Java没有该概念,很多人把字段filed称为属性attribute,不要把成员变量称为属性;

    property:表示状态,但不是字段,是属性的操作方法(getter/setter)决定,框架中使用的大多是属性;

    其中,getter方法只获取某一字段存储的值;

    若操作的字段是boolean类型,则不应称getter方法,而是is方法;

    public <数据类型> get<首字母大写的字段名>(){ return <字段名>; }

    setter方法只设置某一字段的值;

    public void set<首字母大写的字段名>(<数据类型> <形参名>){ <字段名> = <形参名>; }

    每个字段都要提供一对getter/setter,以后使用Eclipse工具后getter/setter自动生成;

    在JavaBean中有属性的概念,只有标准情况下,字段名和属性名相同;

    7 this关键字

    (1)概念

    this表示当前对象;

    (2)this存在于两个位置

    构造器:表示当前创建的对象;

    方法:哪个对象调用this所在的方法,this表示哪个对象;

    如下对含有this的代码进行内存分析

    注:

    当一个对象创建后,JVM会分配一个引用自身的引用this;

    (3)this使用

    a 解决成员变量和参数(局部变量)之间的二义性,必须使用this;

    b 同类中实例方法间互相调用,可省略this,但不建议;

    c 将当前对象作为参数传递给另一个方法;

    d 将this作为方法的返回值(链式方法编程);

    e 构造器重载时的互相调用,this(<实参>);必须写在构造器方法的第一行;

    构造器中只能调用一个重载的构造器;

    如下代码,语法没有错,但存在代码功能重复、代码维护性差的问题;

    class User{ private String name; privte int age; User(){ } User(String name){ this.name = name; } User(String name, int age){ this.name = name; this.age = age; } }

    优化代码如下,this(name)表示调用参数为String类型的构造器;

    class User{ private String name; privte int age; User(){ } User(String name){ this(); this.name = name; } User(String name, int age){ this(name); // 修改处,调用User(Stirng name) this.age = age; } }

    当多个构造器重载时,或多个方法重载时,一般少参数的调用多参数的,因为参数越多,该方法考虑的未知因素越多,即功能更强大;

    class User{ private String name; privte int age; User(){ this(null, 0); } User(String name){ this(name, 0); } User(String name, int age){ this.name = name; this.age = age; } }

    f static不能和this一起使用,因为字节码加载进JVM,static成员便存在,但此时对象还未创建,没有对象就没有this;

    8 构造器和setter方法选用

    (1)创建对象并给对象设置初始值的方式

    a 先通过无参数构造器创建一个对象,再通过对象调用相应的setter方法;

    User u1 = new User(); u1.setName("Sissiy"); u1.setAge(18);

    b 直接调用带参数的构造器,创建出的对象就有初始值;

    User u1 = new User("Sissiy", 18);

    通过构造器和通过setter方法都可完成相同功能;

    (2)给对象设置数据的方式

    a setter注入(属性注入)

    b 构造注入

    (3)方式选择

    若存在带参数的构造器,推荐构造方式;

    若在构建对象时需初始化多个数据,选择构造方式,构造器需要提供多个参数,参数过多不直观,推荐setter方式;

    有时,需要根据数据构建对象,推荐构造方式;

    如圆对象,必须根据半径确定对象,应该在构建圆对象时确定半径值;

    9 练习

    判断点和圆的关系,是圆外?圆中?圆上?

    class Point{ private double x; private double y; Point(double x, double y){ this.x = x; this.y = y; } public double getX(){ return x; } public double getY(){ return y; } } class Circle{ private double rad; private double x; private double y; Circle(){} Circle(double rad, double x, double y){ this.rad = rad; this.x = x; this.y = y; } /* 这是我的思路 public double getDis(double x1, double y1){ return (x1-x) * (x1-x) + (y-y1) * (y-y1); } */ // 老师的思路 public double getDis(Point p){ return p.getX() * p.getX() + p.getY() * p.getY(); } public int judge(double dis){ if(dis * dis == rad){ return 0; // 圆上 }else if(dis > rad){ return 1; // 圆外 }else{ return 2; // 圆内 } } } public class PointDemo{ public static void main(String[] args){ Point p1 = new Point(3, 4); Circle c1 = new Circle(1, 0, 0); // double dis = c1.getDis(p1.getX, p1.getY); double dis = c1.getDis(p1); switch(c1.judge(dis)){ case 0: System.out.println("圆上"); break; case 1: System.out.println("圆外"); break; case 2: System.out.println("圆内"); break; } } }

    转载请注明原文地址: https://ju.6miu.com/read-600259.html

    最新回复(0)