Java面向对象

文章目录

  • Java面向对象
    • 1. 方法
    • 2. 构造方法
    • 3. 方法重载
    • 4. 继承
    • 5. 多态
    • 6. 抽象类
    • 7. 接口
    • 8. 静态字段、方法
    • 9. 包
    • 10. 内部类

1. 方法

  1. private修饰field;

  2. 外部通过public的getXxx()和setXxx()方法;

  3. this变量:方法内部的this始终指向当前该类的实例

  4. 可变参数:类型... 名称,如func(String… names),相当于数组类型

    使用可变参数的好处:
    1. 如果使用数组,需要先构建数组,而使用可变参数不需要
    2. 使用数组可以传入null,但可变参数在传入0个参数时,接受的是空数组而不是null

  5. 传递参数时:

    • 基本类型参数,是值的复制,互不影响
    • 引用类型参数:调用方和接收方指向的是堆内存的同一个对象或数组

2. 构造方法

  1. 通过构造方法初始化实例
  2. 构造方法没有返回值(void也没有),方法名和类名相同,通过new来调用;
  3. 默认有空参构造器,定义有参的,空参的就没了;
  4. 引用类型未初始化是null,int是0,布尔是false
  5. 为方便代码复用,可在构造器内调用其他构造器; this(age,“lzh”);

3. 方法重载

  1. 类中一系列方法的功能类似,只是参数不同,可将其进行方法重载
  2. 方法名相同,返回值通常相同,如int indexOf(int ch); int indexOf(String str);

4. 继承

  1. extends将Student继承Person,会获得Peson的所有功能:字段和方法。父类中严禁定义同名字段;
  2. 一个类没有extends时,会默认继承Object
  3. 子类继承了所有(不包括构造方法),但是无法访问私有字段、私有方法
  4. 父类字段改为protected字段可以被子类访问;protected字段和方法被子类所访问;
  5. super:表示父类;子类引用父类字段时:super.fieldName,因为子类继承后拥有该字段,也可写成this.fieldName;
  6. 任何类的构造器的第一句必须是父类的构造器,默认省略了super();所以父类要有无参构造器或显示调用其他super;
  7. 阻止继承:final class,表示该类不能继承
  8. 向上转型:People变量可以指向Student实例,People p = new Student();

因为Student继承Person,拥有Person全部功能。
这样 子类类型 => 父类类型

  1. 向下转型:父类类型强制转换为子类类型,Student s = (Student)p;
    若p指向的就是People类型,则向下转型失败,因为p原本就是父类类型,子类多的功能无法实现;
    instanceof操作符:判断一个实例是不是某个类型(或者该类型的父类型)
Person p = new Student();if(p instanceof Stundet){Student s = (Student)p;}
  1. 子类和父类是is关系,使用继承;子类和某个类是has关系,应把它作为子类的一个字段

5. 多态

  1. 方法重写:子类定义了一个和父类方法签名完全相同的方法
  2. Person p = new Student();如果子类重写父类run方法,那么p调用的是子类的run方法

实例方法是基于`运行时的实际类型的多态调用,而不是变量的声明类型

  1. 多态:某个类型的方法调用,其真正执行的方法取决于运行时期的实际类型的方法
public void runTwice(Person p) {p.run();p.run();//无法知道传入的参数实际类型究竟是Person,还是Student.//当传入可变参数时,我们不需要知道它们属于什么类型,只需要知道调用run方法会调用它实际类型的run}

多态允许添加更多类型的子类实现;

  1. 重写Object方法:

    • toString()
    • equals()
    • hashCode()
  2. 在子类中调用父类被重写的方法:super.run();

  3. 使用final设置一个父类方法不能被子类重写;final修饰的字段不能被修改

6. 抽象类

  1. 如果父类的被重写方法run(),没有实际意义,不需要实现任何功能,只是为了定义方法签名以供子类去重写,可将其声明为抽象方法:abstract void run(); 没有方法体
  2. 抽象方法无法执行,那么所在的类就无法实例化,所以类也要声明为抽象类:abstract class People{ };
  3. 设计抽象类就是用于继承,抽象类强迫子类实现抽象方法。抽象方法相当于定义了规范
  4. 面向抽象编程:使用抽象类去引用具体的子类,尽量引用高层类型,避免引用实际子类型
Person s = new Student();Person t = new Teacher();// 不关心Person变量的具体子类型:s.run();t.run();

7. 接口

  1. 抽象类中可以有字段、非抽象方法,但接口中没有字段,全都是抽象方法;
 interface Person{void run();String getName();}
  1. 接口是比抽象类还抽象的纯抽象接口,方法都是public abstract,所以这俩可以不写
  2. 类是实现接口:implements,接口可以多实现;
  3. 特殊的是:接口可以定义常量、default方法
  4. 接口interface之间也可以继承,相当于扩展了接口的方法
  5. 一般来说,公共逻辑适合放在abstract class中,具体逻辑放到各个子类,而接口层次代表抽象程度。
    在使用的时候,实例化的对象永远只能是某个具体的子类但总是通过接口去引用它,因为接口比抽象类更抽象:
List list = new ArrayList(); // 用List接口引用具体子类的实例Collection coll = list; // 向上转型为Collection接口Iterable it = coll; // 向上转型为Iterable接口
  1. 接口的default方法:default void run(){ }; 实现类不必重写default方法;
    目的:给接口新增一个方法时,要修改全部实现子类,所以新增一个default方法,这样子类可以不重写该方法,如果需要,也可以重写;

8. 静态字段、方法

  1. 前面class里定义的都是实例字段:每个实例都有独立的字段,每个实例之间互不影响
  2. 静态字段:static修饰;一个所有该类的实例共享的字段
  3. 使用 类名.static字段 来访问静态字段;
  4. 静态方法:static void run(){}; 使用类名调用
    因为静态方法属于类自身,所以静态方法内部无法使用this、无法访问实例字段,只能访问静态字段
    常用于工具类Arrays.sort()、Math.random(),辅助方法:main()方法;

9. 包

  1. 解决类名冲突,如不同人都写了个Person类
  2. 一个类总属于某个包,比如Person只是简写的类名,全名应是 包名.Person
  3. 在定义class的首行声明该类所在的包
  4. JVM运行时,看的是全类名,包不同,类就不同;

包没有父子关系,java.util和java.util.zip是不同的包

  1. 包作用域:同一个包的类,可以访问包作用域的字段方法;缺省就表示包作用域(默认)
  2. import:类中引用其他class,使用import导入该类,之后就可以写简单类名了;否则使用时要写全类名;
  3. 编译器编译时,遇到一个class,如果是完整类名就直接查找;如果是简单类名就按顺序:
    1. 先查找当前package是否有该类;
    2. 再查找import导入的包中是否有;
    3. 最后在java.lang包中查找
      因此,编译器编译时自动导入当前package的其他class、java.lang.*(不包括lang包内部的包,java.lang.reflect这些)

10. 内部类

  1. 被定义在另一个类的内部;不能单独存在,必须依附一个外部类的实例
  2. 先创建Outer实例,再通过Outer.new来调用内部类的构造器:Outer.Inner inner = outer.new Inner();
  3. Inner Class有this指向自己,还有一个Outer.this指向外部实例;
  4. Inner Class可以修改外部类的private字段方法
  5. 匿名类:不需要在外部类中明确定义;在方法内部使用匿名类来定义(局部内部类)
    通常是实例化接口,但接口不能实例化,需要实例化该接口的一个实现类,而该实现类名字不重要;
Runnable r = new Runnable(){//实现必要的抽象方法。。。}