java面向对象
成员 = 属性 + 方法
类和对象内存分配机制
- 栈:基本数据类型
- 堆:对象(数组)
- 方法区:常量池(如字符串),类加载信息
Person p = new Person();
- 加载Person类信息(属性和方法信息,只会加载一次)
- 堆中分配空间,默认初始化
- 堆地址赋给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 |
封装
私有 get set,set中可以进行安全性检查
继承
extends还有依赖组合聚合
- 子类继承父类方法变量(一般要继承的方法写成public,变量才写私有的)
- 所有类都默认继承Object类(java.lang.Object)(有equals,hashcode等方法)
- java类只能单继承 A->B->C 不能A->B,C
- 子类每个构造器都会默认调用父类无参构造器,可以显式更改super(1),super只能放第一行
多态
1、类型转换
子类重写父类方法,父类引用指向子类对象
- 方法名,参数列表必须相同,返回类型只能缩小不能扩大
- 修饰符可以扩大不能缩写 public>protected>default>private
- 抛出的异常可以缩小不能扩大,
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可以被类直接调用,抽象方法没有方法体,矛盾
代码块(初始化块)
类什么时候会被加载
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时
//只在加载类或者创建对象时被隐式调用
[修饰符]{//修饰符只能为static(静态代码块)或空(普通代码块)
代码
};
//相当于另一种形式的构造器。多个构造器的重复语句可以抽取到代码块中
- static代码块:类加载时执行且只执行一次
- 普通代码块:每创建一个对象都执行一次
- 静态代码块只能调用静态成员,普通代码块可以调用任意成员
- 只使用类的静态成员时,普通代码块并不会执行
- 不管调用哪个构造器都会先执行代码块,构造器隐含了super()和普通代码块
public A(){ //super() //普通代码块 do something }
- 代码块优先于构造器
- 掉用子类的static变量时也会加载父类代码块
执行顺序
创建一个对象时,一个类的调用顺序:
- 父类静态代码块和静态属性初始化(两者优先级一样,看定义顺序)
- 子类静态代码块和静态属性初始化(同优先级)
- 父类普通代码块和普通属性初始化(同优先级)
- 父类构造方法
- 子类普通代码块和普通属性初始化(同优先级)
- 子类构造方法
接口
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多个别的接口。
- 接口多态
内部类
类的第五大成员(属性,方法,构造器代码)
最大的特点就是可以访问私有属性
class Outer{//称为外部类
int age;
class Inner{//内部类
}
}
-
局部内部类:定义在局部位置比如方法中
- 可以直接访问外部类的所有成员,包括私有的。(直接age或重名时Outer.this.age)
- 作用域在定义它的方法体内。
- 不能添加访问修饰符,但是可以用final(不然可以被继承),因为相当于局部变量。
-
匿名内部类:很常见,简化代码编写
- 可以直接访问外部类的所有成员,同局部内部类。
- 格式:new 类名或接口名(参数列表){重写方法;};
//通过tiger.getclass()可以发现底层会分配类名Outer$1 //相当于class Outer$1 implement IA(){} IA tiger = new IA(){ public void cry(){ sout("im a tiger"); } }; tiger.cry(); //new 类: //相当于class Outer$2 extends Cat{} //编译类型是Cat,运行类型是Outer$2 Cat f = new Cat("kitty"){ public void test(){...}//重写了方法 } f.test();
class Cat(){ public Cat(String name){} public void test(){} } interface IA(){public void cry();}
- 常用实践:当作实参直接传递。 f(new 接口(){重写方法;})
把(定义子类,重写接口中的方法,创建子类对象,调用重写后的方法)简化成一步。 -
特点的话,除了只能使用一次,其实还有其他用处(在看spring-boot源码时发现的)当你想使用一个类的protected 方法时,但是又不和这个类在同一个包下,你是没办法调用的。这时候匿名类就派上用场了,你可以声明一个匿名类继承该类,并定义一个方法,在这个方法内使用super调用你想调用的那个方法(其实你也可以写个类继承这个类,就能调用父类的protected方法了,但是匿名类更简洁,因为你只想调用这个方法而已)
-
成员内部类:定义在成员位置。
- 同样可以直接访问外部类的所有成员,包括私有
class Outer { public int age = 18; class Inner { public int age = 20; public viod showAge() { int age = 25; System.out.println(age);//25 System.out.println(this.age);//20 System.out.println(Outer.this.age);//18 } } }
- 4个访问修饰符都可以,因为是成员。
- Outer方法中可以创建实例调用Inner
- 外部其他类调用:
Outer outer8 = new Outer()
outer8.Inner inner = outer8.new Inner();
-
静态内部类
- 跟成员内部类相比:static,只能访问静态成员。
其他
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;
}
}