java面向对象


目录
    • 类和对象内存分配机制
    • 构造器
    • 访问修饰符:
    • 封装
    • 继承
    • 多态
      • 1、类型转换
      • 2、多态参数
      • 3、动态绑定机制
      • 总结:
    • static静态成员
    • final
    • abstract抽象类
    • 代码块(初始化块)
      • 类什么时候会被加载
      • 执行顺序
    • 接口
    • 其他
  • 设计模式
    • 单例设计模式
      • 饿汉式
      • 懒汉式
    • 模板设计模式

成员 = 属性 + 方法

类和对象内存分配机制

  1. 栈:基本数据类型
  2. 堆:对象(数组)
  3. 方法区:常量池(如字符串),类加载信息
  4. Person p = new Person();
    1. 加载Person类信息(属性和方法信息,只会加载一次
    2. 堆中分配空间,默认初始化
    3. 堆地址赋给p

构造器

构造方法可以处于public、protected、private和默认四种访问级别之一
没有定义构造器,系统自动默认构造方法如Person(){}
一但用户定义,默认构造器就被覆盖了,还想用无参构造器要显式定义

访问修饰符:

4种修饰符来修饰类属性和类方法。修饰类只有默认和public

级别 修饰符 同类 同包 子类 不同包
公开 public 1 1 1 1
保护 protected 1 1 1 0
默认 1 1 0 0
私有 private 1 0 0 0

封装

私有 getset,set中可以进行安全性检查

继承

extends还有依赖组合聚合

  • 子类继承父类方法变量(一般要继承的方法写成public,变量才写私有的)
  • 所有类都默认继承Object类(java.lang.Object)(有equals,hashcode等方法)
  • java类只能单继承 A->B->C 不能A->B,C
  • 子类每个构造器都会默认调用父类无参构造器,可以显式更改super(1),super只能放第一行

多态

1、类型转换

子类重写父类方法,父类引用指向子类对象

  1. 方法名,参数列表必须相同,返回类型只能缩小不能扩大
  2. 修饰符可以扩大不能缩写 public >protected>default>private
  3. 抛出的异常可以缩小不能扩大,
Person p = new Student();	//父类的引用可以指向子类的实现  
p.test();  			//im a student,子类重写了父类方法就掉用子类的  
p.exam();			//报错 父类没有exam方法, 
//编译类型Person ,运行类型Student
Animal[] a =new Animal[3];
a[0]=new Animal();
a[1]=new Kitten();
a[2]=new Puppy();
for (int i = 0; i < 3; i++) 
    show(a)

2、多态参数

形参父类,实参子类

void show(Animal a){
    a.say()
    if(a instanceof Kitten)
        ...
}

3、动态绑定机制

当调用对象方法时,该方法会和该对象内存地址(运行类型)绑定。属性则不会。

A extends B  
A:int i=10; geti();sum(){return geti()+1}  
B:int i=20; geti();sum(){return geti()+1}  
B b=new A()
b.sum()//11
A extends B  
A:int i=10; geti();sum(){return i+1}  
B:int i=20; geti();sum(){return i+1}  
B b=new A()
b.sum()//21

总结:

1、编译类型,运行类型,调方法顺运行类型向上查找。
2、强转只能转父类引用(而非对象),它必须指向的是要转的类型。否则ClassCastException
3、属性没有重写,只看编译类型
4、a instanceof B 判断a的运行类型是不是B的子类型 A extends B , A a = new B() a instanceof B :true

static静态成员

public static int count = 0;
public static int sum(int a,int b);

  • 访问规则同非静态的,各实例共享,没有实例也能访问。推荐用类名访问
  • 静态方法只能访问静态成员,不能使用this
    非静态方法可以访问静态成员
  • 静态方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区
  • jdk8以前存放在方法区静态域,jdk8后放在堆,跟反射机制有关
  • 不涉及实例的成员时可以设计成静态方法提高效率

final

可修饰类,类成员,局部变量
final类不可被继承,final方法不可被子类重写,final属性,final局部变量不可修改

  • final 属性在定义时(可在定义时,代码块,构造器中)赋值,之后不能再修改。

    final变量定义的时候,可以先声明,而不给初值。这样final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。提供了更大的灵活性

  • 如果是final static属性,只能在定义时,静态代码块中赋值
  • final类方法就可以不用写final了,因为没有子类重写。(final类中的方法默认是final的。)
  • final不能修饰构造方法,因为构造函数不是继承而来的
  • final static搭配效率更高,编译器做了优化,调用时不会导致类加载
  • 注意final 对象(修饰了引用)
        public void change(final int a) {
            a++;//报错
        }
        public void change(final A a) {
            a.i=5;//ok
            a = new A();//改变了a的值,报错
        }
    

abstract抽象类

abstract只修饰类和方法
public abstract void eat(); //没有方法体

  • 父类方法不确定时,只需声明不用实现。留给子类重写。有抽象方法必须声明为抽象类
  • 抽象类不能实例化
  • 抽象类不一定要有抽象方法
  • 子类继承了抽象类,必须实现抽象类所有的抽象方法,除非它自己声明为抽象类。
  • 抽象方法不能用private,final,static修饰

    static可以被类直接调用,抽象方法没有方法体,矛盾

代码块(初始化块)

类什么时候会被加载

  1. 创建对象实例时(new)
  2. 创建子类对象实例,父类也会被加载
  3. 使用类的静态成员时
//只在加载类或者创建对象时被隐式调用
[修饰符]{//修饰符只能为static(静态代码块)或空(普通代码块)
    代码
};
//相当于另一种形式的构造器。多个构造器的重复语句可以抽取到代码块中
  • static代码块:类加载时执行且只执行一次
  • 普通代码块:每创建一个对象都执行一次
  • 静态代码块只能调用静态成员,普通代码块可以调用任意成员
  • 只使用类的静态成员时,普通代码块并不会执行
  • 不管调用哪个构造器都会先执行代码块,构造器隐含了super()和普通代码块
    public A(){
        //super()
        //普通代码块
        do something
    }
    
  • 代码块优先于构造器
  • 掉用子类的static变量时也会加载父类代码块

执行顺序

创建一个对象时,一个类的调用顺序:

  1. 父类静态代码块和静态属性初始化(两者优先级一样,看定义顺序)
  2. 子类静态代码块和静态属性初始化(同优先级)
  3. 父类普通代码块和普通属性初始化(同优先级)
  4. 父类构造方法
  5. 子类普通代码块和普通属性初始化(同优先级)
  6. 子类构造方法

接口

public interface USB{
    public void start();//规定接口方法
}
public class Phone implements USB{
    public void start(){
        sout('convert file...');//实现接口方法
    }
}
public class Camera implements USB{
    public void start(){
        sout('convert photo...');//实现接口方法
    }
}
public class Computer implements USB{
    public void work(USB usb){
        usb.start();
    }
    public static void main(String args[]){
        Computer computer = new Computer();
        Phone phone = new Phone();
        Camera camera = new Camera();
        computer.work(phone);//convert file...
        computer.work(camera);//convert photo...

        //接口多态
        USB phone = new Phone();
        USB camera = new Camera();
        usbs = [phone, camera]
        for usb in usbs:
            usb.work();//动态绑定
            if usb instanceof ...
                ...
    }
}

  • 接口修饰符只能是public和默认,跟class相同
  • 接口属性隐含且只能是public final static。 且必须初始化int a = 1 //等价于public final static int a = 1
  • 接口方法默认隐含且只能是public。隐含abstarct,不能实例化
  • 继承接口要实现接口的所有抽象方法(除非A是抽象类)

    jdk8前,接口类所有方法都得是抽象方法,不能有方法体
    jdk8后,接口类可以有静态方法,默认方法(需要关键字default修饰)。

  • 一个类可以同时实现多个接口
  • 接口不能继承其他类,但可以继承extends多个别的接口。
  • 接口多态

其他

Object
getclass() 返回运行时
clone() 返回复制副本
finalize() 对象被回收时自动调用该方法。子类可以重写做一些释放资源的工作。jvm没有引用时回收,可以System.gc()显式回收
hashcode()
toString() 打印或拼接时会调用类的toString,子类往往重写
==基本类型判断值,引用类型判断地址

设计模式

单例设计模式

一个类只能存在一个对象实例。

饿汉式

class SingleTon{
    //1. 构造器私有化,防止直接new
    private SingleTon(){}
    //2. 类的内部创建对象实例
    private static SingleTon instance = new SingleTon();
    // 3. 对外暴露一个静态公共方法
    public static getInstance(){
        return instance;
    }
}

缺点: 类加载时就创建了实例 (例如只是调用一下静态成员就new了实例 ),浪费资源。

懒汉式

用时再创建,克服饿汉缺点,但线程不安全

class SingleTon{
    private SingleTon(){}
    private static SingleTon instance ;
    public static getInstance(){
        if(singleTon == null)
            instance = new SingleTon();
        return instance;
        //这三步线程不安全,可以创建多个实例
    }
}

模板设计模式

(抽象类实践)
需求:多个类完成不同任务,并统计各自完成任务的时间

abstract class Template{
    public abstract void job();
    public double caltime(){
        time start;
        job();//动态绑定机制
        time end;
        return end - start;
    }
}