09-面向对象

nobility 发布于 2021-03-05 2013 次阅读


面向对象

类和对象

class关键字

一个java文件可以有多个class,但是只能有一个public修饰的类,且必须与文件名相同

运行时,类存在方法区

new关键字

执行过程
  1. 分配对象空间,并将对象成员变量初始化为各式各样的零
  2. 执行属性值的显示初始化,即类中属性显示赋值
  3. 执行构造方法
  4. 返回对象的地址

运行时,对象在堆内存

匿名对象

在调用方法的时候只能使用唯一一次,省略了对象名,

内部类

成员内部类
  • 位置:与成员变量地位一样,外部类内部,方法外部
  • 编译:内部类也会单独编译成一个class字节码文件,命名以外部类名$外部类名形式命名
  • 访问作用域:
    1. 内部类访问外部类成员,没有任何限制,需要外部类名.this.成员名形式访问
    2. 外部类访问内部类成员,需要借助内部类对象
    3. 其他地方访问内部类成员,需要借助内部类对象,创建内部类对象的语法外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
静态内部类
  • 是特殊的成员内部类,用static修饰
  • 访问作用域:
    1. 内部类只能直接访问外部类的静态成员,访问非静态成员需要借助外部类对象
    2. 外部类访问内部静态成员,直接内部类.成员,非静态类成员,需要借助内部类对象
    3. 其他地方访问内部类普通成员,需要借助内部类对象,创建内部类对象的语法外部类名.内部类名 对象名 = new 外部类名.内部类名();,其他地方访问内部类的静态成员,直接外部类.内部类.成员即可
局部内部类
  • 位置:与局部变量地位一样,方法内
  • 访问作用域
    1. 内部类访问方法局部变量,该变量必须是有效final的,即该变量的值只能赋值一次
    2. 方法中访问内部类成员,需要借助内部类对象
    3. 其他地方无法使用该内部类,只能在当前方法中使用
匿名内部类
  • 是特殊的局部内部类
  • 在创建对象的时候只能使用唯一一次,省略了实现类和实现类名
  • 使用方式:new 接口名(){...实现接口的方法};
    1. 左部接口名,对象自动上转型
    2. new是创建匿名内部类对象动作
    3. 右部接口名是要实现的接口
    4. {...}内部类的内容
  • 编译:匿名内部类也会单独编译成一个class字节码文件,命名以外部类名$编号形式命名
Lambda表达式
  • 只有接口并且只有一个抽象方法(可以有其他私有方法、默认方法和静态方法)时才能使用Lambda表达式,使用时必须注意上下文环境
  • 使用方式:接口名 对象名 = (参数列表)->{方法体}
  • 省略模式
    • 参数类型可省,但是要省都省,不能省一部分
    • 参数只有一个下括号可省
    • 方法体只有一条语句,大括号可省略,包括return
  • 编译:Lambda表达式不会生成静态的字节码文件,在程序运行是动态生成字节码文件
方法引用

方法引用多用来作为方法作为参数时使用,也就是说参数要传入的是函数式接口,需要写Lambda表达式时,正好有写好的方法,就可以使用方法引用,将该方法作为要传入的方法

引用方式 格式 参数说明
静态方法引用 类名::静态方法名 全部对应
成员方法引用(已实例化) 对象::成员方法名 全部对应
类成员方法引用(未实例化) 类名::成员方法名 首个参数是类实例
之后参数开始依次对应
构造方法引用 类名::new 全部对应

类的权限修饰符

public protected (default) private
外部类 yes yes
成员内部类 yes yes yes yes
局部内部类

封装

构造方法

  1. 特殊的方法,再创建对象的时候调用,用来做对象的初始化,与类名一致,且不能写返回数据类型,也可以重载

    public class Test {
      int id;
      public Test(int id){ this.id = id; }
      public Test(){ }
    //  public int Test(){ }	//报错,不能写返回数据类型
    }
    
  2. 通过new关键字调用

    Test test = new Test();	//调用构造方法
    Test test1 = new Test(1);	//调用构造方法的重载方法
    
  3. 相当于无返回值的方法,无需写返回类型和void,所以可以有空return,不能return某个值

    public class Test {
      int id;
      public Test(int id){ 
        this.id = id; 
        return;	//可空返回
      }
      //public Test(){ return true;}	//不能返回数据类型
    }
    
    
  4. 若不写构造方法,编译器自动加一个无参构造方法,若写构造方法则不会自动添加

    public class Test {
      int id;
      public Test(int id){ this.id = id; return;}
      public static void main(String[] args){
        //new Test();	//报错,已写构造方法,不会自动添加
      }
    }
    
    public class Test {
      int id;
      public static void main(String[] args){
        new Test();	//不写构造方法,自动添加无参构造
      }
    }
    

this关键字

  1. 类中若不写构造方法则编译器会自动添加一个无参构造,若写上则不会自动添加
  2. this本质是该类中所有方法的隐式参数,存放指向创建好的对象的指针,即指向当前对象
  3. 避免二义性来使用,区别成员变量和局部变量
  4. 只能在构造方法中调用重载的构造方法,且必须在第一行,必须有出口
  5. 不能在static修饰符修饰的方法中使用

static修饰符

  1. 用static修饰符修饰的属性和方法属于类,所有所有对象共享,再类加载后显示初始化
  2. 可以用类名调用,也可以通过对象调用,建议类名调用
  3. 静态方法中不能使用this关键字,静态方法只能直接调用静态方法和属性
  4. 非静态方法可以使用this关键字,能直接访问静态方法和属性,也能直接访问非静态方法和属性

访问控制修饰符

  • 访问的两种形式
    1. 通过new出对象点的形式
    2. 子类继承父类。子类通过super关键字点的形式

private:同一个类两种形式都行,出了类都不行

default:同一个包两种形式都行,出了包都不行

protected:同一个包两种形式都行,出了包只有第二种形式可以,若又用static修饰后,则子类两种形式都行,若不是子类两种都不行

修饰符 同一个类 同一个包 不同包的子类 不同包非子类
private yes
(default) yes yes
protected yes yes yes
public yes yes yes yes

javaBean标准

  1. 必须是一个public类
  2. 必须有一个空的构造方法
  3. 属性使用private访问权限
  4. 为每个属性提供set/get方法访问相关属性,这些方法是public修饰
  5. getter/seter方法命名规范
    1. 若字段是布尔类型,把get改为is命名
    2. 若字段名第二个字母大写,直接使用get/set字段名即可
    3. 若字段名全部大写,直接使用get/set字段名即可

继承

extends关键字

  • 父类别名超类、基类、派生类
  • 类中只有单继承,没有多继承,接口有多继承
  • 子类继承父类,可以得到父类的全部属性和方法(构造方法除外),但是能不能访问得看控制访问修饰符
  • 未指定父类,默认父类是java.lang.Object

Object类

toString方法
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 无参,返回全类名@对象的哈希地址
  • 打印对象默认调用的是toString方法
  • 重写toString方法,一般是打印输出对象的各个属性值
equals方法
public boolean equals(Object obj) {
    return (this == obj);
}
  • 参数是Object类型,返回两个对象地址是否相同的布尔值
  • 重写equals方法,一般判断两个对象是否逻辑相等,就是各个属性是否相等
public boolean equals(Object o) {
  if (this == o) return true;
  //若是同一个对象地址,则是同一个对象,一定相等
  if (o == null || getClass() != o.getClass()) return false;
  //若传入对象是null,或两个对象不是同一个类,必定不相等
  Test test = (Test) o;	//将Object强制下转型为当前类的类型
  return id == test.id &&
      name.equals(testC.name);
  //比较每个属性值,引用类型用equals方法,基本类型用==
  //有可能引用数据类型封装了属性,使用get方法进行比较
  //若有一项不等则认为两个对象逻辑上不一样,全部相等,则认为逻辑上全等
}
clone方法
  • 将堆内存的对象复制一份,地址会不相同
  • 由于使用protected修饰,所以需要子类重写该方法调用父类的clone方法才能实现克隆,要注意的是返回的是Object类形,在接收时需要强转
  • 虽然已经可以调用克隆方法了,但是若不实现克隆标记接口Cloneable也会抛出异常
protected native Object clone() throws CloneNotSupportedException;
finalize方法
  • 当对象被回收前调用,由于该方法抛出的是错误,而不是异常,原因是对于要回收的对象而言,回收方法内部抛出异常毫无影响
  • 由于对象回收时调用该方法可能延时、死锁等问题导致对象回收失败,jdk1.9之后此方法不再建议使用
rotected void finalize() throws Throwable;
  • jdk1.9后使用如下方法代替手动回收,解决回收延误,死锁等问题
import java.lang.ref.Cleaner;

public class Main{
  public static void main(String[] args) {
    Test c = new Test();
    try(TestCleaner testCleaner = new TestCleaner(c)){
      //创建指定对象的回收器,将要回收的对象传入,执行完这段代码后自动回收
    }catch (Exception e){
      e.printStackTrace();
    }
  }
}
class Test implements Runnable{
  public Test() {
    System.out.println("创建");
  }
  @Override
  public void run() {	//回收时调用该线程run方法
    System.out.println("回收");
  }
}

class TestCleaner implements AutoCloseable{
  //回收器
  private final Cleaner.Cleanable cleanable;  //存放注册后的回收对象的盒子
  
  public TestCleaner(Test c){ //将要回收的对象传入回收器的构造方法
    final Cleaner cleaner = Cleaner.create(); //创建回收器对象
    this.cleanable = cleaner.register(this,c);  //使用回收器对象注册可该要回收对象,放入回收盒中
  }
  @Override
  public void close() throws Exception {
    //在close方法中执行回收操作
    this.cleanable.clean(); //回收操作
  }
}
Objects.equals方法
public static boolean equals(Object a,Object b) {
  return a == b || (a != null && a.equals(b));
  //该工具类方法能有效避免空指针异常比较两个对象是否相等
}

public static <T> T requireNonNull(T obj) {
  //重载方法的第二个参数可以设置字符串类型的错误信息
    if (obj == null)
        throw new NullPointerException();
  //该工具类方法能对对象进行空指针判断,若有空指针则抛出异常
  //用于校验参数是否是空指针
    return obj;
}

方法的重写

  1. 发生在继承关系中,子类重写父类方法
  2. 方法名和参数列表完全一致
  3. 返回值要求,子类返回值数据类型范围小于等于父类返回值范围
  4. 访问修饰符要求,子类方法权限大于等于父类方法访问权限

super关键字

  1. 子类构造方法中若没显式调用父类构造,则默认第一句super();调用父类无参构造方法,若父类还没有无参构造方法,则必须用super关键字显式调用父类构造,所以先执行父类构造,后执行子类构造
  2. super和this都是存放指向创建好的对象的指针,即指向当前对象,只不过super是指向父类成员的引用,this是指向子类成员的引用
  3. 中避免二义性来使用,区别子类成员和继承的父类成员和局部变量
  4. 只能在子类构造方法中调用父类构造方法,且必须在第一行
  5. 不能在static修饰符修饰的方法中使用

final修饰符

  1. 修饰变量:变为符号常量,对于引用类型来说,是地址不能改变
  2. 修饰成员变量:必须声明时赋值或所有重载构造中赋值,并且不能定义set该字段方法,变为符号常量
  3. 修饰方法:不能被重写,可以被重载(不能修饰抽象方法)
  4. 修饰类:不能被继承

多态

产生多态的条件

  • 发生多态的三个必要条件:多个子类继承父类或实现类实现接口,多个子类都重写了父类方法,父类引用指向的是子类对象
  • 多态发生在方法上的,引用指向的对象是谁,就是调用谁重写的方法,若当前没有重写该方法则才会向父类找,直到到Object类;编译看左部数据类型,运行看右部具体对象是谁
  • 多态不会发生属性上,属性只会是当前引用对象类型的属性值,若当前没有重写该属性则才会向父类找,直到到Object类;编译和运行都看左部数据类型是谁

父类引用指向的是子类对象

  • 该父类可以是普通父类
  • 该父类可以是抽象类
  • 该父类可以是接口

对象的转型

  • 父类引用指向子类对象,即子类对象转为父类对象,一定是安全的,所以向上自动转型

    • 向上自动转换:编译时就会检查出类型转换错误
  • 将上转型后的引用无法使用子类方法,需要还原回子类对象才能使用,可能会转错是不安全的,所以需要强制转换,即父类引用指向子类对象还原为子类引用,向下强制转型

    • 向下强制转换:运行时才会检查出错误,ClassCastException类型转换异常
  • 一般先判断父类引用指向的子类具体对象是谁,使用父类引用指向子类对象的变量 instanceof 子类,返回布尔值,若是返回true,再进行强制向下转型进行调用方法

代码块

普通代码块

在方法内,顺序执行,起到变量的块级作用域分隔作用

public static void main(String[] args) {
  { int a = 0; }	//这个a遇到花括号后生命周期结束
  //System.out.println(a);	//报错,上个a的生命周期已结束,找不到a变量
	int a = 0;	//新声明的变量,作用域是从声明处到整个main方法结束
	//{ int a = 0; }	//报错,上一个a声明周期未结束,声明同名变量
}

构造代码块

  1. 在类内方法外,调用构造方法一次执行一次,构造方法之前执行
  2. 若父类也有构造代码块,则先执行父类的构造代码块
  3. 可以调用静态属性和方法,也可以访问非静态属性和方法
  4. 当有多个构造代码块时,各个构造代码块之间顺序执行

静态代码块

  1. 在类内方法外并且用static修饰,类加载时执行一次,构造代码块之前执行
  2. 若父类也有静态代码块,则先执行父类的静态代码块
  3. 只能访问静态方法和属性
  4. 当有多个静态代码块时,各个构造静态块之间顺序执行

抽象类和接口

abstract修饰符

  1. 不能与final修饰符同时使用,抽象方法本来就是为了子类实现,加上final没有意义

  2. 修饰类:该类就是抽象类

    1. 抽象类不能实例化,只能被继承
    2. 抽象类可以包含普通类的一切,但是构造方法不能用new来实例化,只能给子类用super调用
  3. 修饰方法:该方法就是抽象方法

    1. 抽象方法只能出现在抽象类和接口中
    2. 没有方法体,所以非抽象子类继承必须重写抽象方法

interface修饰符

代替class关键字声明的是接口

  • 接口中只能定义常量(不写修饰符也默认加上public static funal)和抽象方法(不写修饰符也默认加上public abstract
  • jdk8 后新增:都拥有方法体了所以不能加abstract
    • 默认方法:default修饰符修饰的方法,拥有方法体,实现类可以不实现该方法,实现类对象可以调用该方法
    • 静态方法:static修饰符修饰的方法,拥有方法体,因为可以多实现接口,可能会有静态方法冲突,所以不能通过接口实现类来调用接口静态方法,只能通过接口名点的形式调用
  • jdk9 后新增:都拥有方法体了所以不能加abstract
    • 普通私有方法:private修饰,拥有方法体,实现类不能调用私有方法,解决多个普通方法之间调用问题
    • 静态私有方法:private static修饰,拥有方法体,不能通过接口名点形式调用,解决多个静态方法之间调用问题

接口的多继承

  • 若子接口多继承中有抽象方法冲突,等于没冲突
  • 若子接口多继承中有默认方法冲突,子接口必须重写该默认方法

implements关键字

  • 用于类实现接口,可以多实现,用逗号分隔
  • 和继承类似,实现类拥有接口的一切,并且非抽象实现类必须实现接口的所有抽象方法
  • 若实现类多实现中有抽象方法冲突,则实现类中重写一次即可
  • 若实现类多实现中有默认方法冲突,则必须在实现类中重写该默认方法
  • 继承父类方法和实现接口的默认方法冲突,优先继承父类的方法
此作者没有提供个人介绍
最后更新于 2021-03-05