封装
定义:隐藏对象的内部实现细节,控制对象访问与修改
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
过滤有效数据
private int score;
public void setScore(int score) {
//当分数大于0且小于等于100时为有效数据
//进行正常赋值
if (score>0&&score<=100) {
this.score = score;
}else {
this.score = 0;
}
}
public int getScore() {
return this.score;
}
public static void main(String[] args) {
Student student = new Student();
student.setScore(150);
System.out.println(student.getScore());
}
打印结果为:
0
get/set方法是外界访问对象私有属性的唯一通道,方法内部可对数据进行检测和过滤。
继承
语法:
class子类extends 父类{}//定义子类时,显示继承父类
应用:
产生继承关系之后,子类可以使用父类中的属性和方法,也可定义子类独有的属性和方法。
好处:
既提高代码的复用性,又提高代码的可扩展性。
继承的特点:
Java为单继承,一个类只能拥有一个直接父类,但可以多级继承,属性和方法逐级叠加。
什么不可被继承:
- 构造方法:
- 类中的构造方法,只负责创建本类对象,不可继承。
- private修饰的属性和方法:
- 访问修饰符的一种,仅本类可见。(详见下图)
- 父子类不在同一package中时,default修饰的属性和方法:
- 访问修饰符的一种,仅同包可见。(详见下图)
方法的重写
- 方法重写的规则:
- 方法名称、参数列表、返回值类型必须与父类相同。
- 访问修饰符可与父类相同或是比父类更宽泛。
- 方法重写的执行:
- 子类重写父类方法后,调用时优先执行子类重写后的方法。
super关键字
super关键字可在子类中访问父类中的方法
继承中的对象创建
- 在具有继承关系的对象创建中,构建子类对象会先构建父类对象。
例如:
public class Person {
public Person() {
System.out.println("我是人类中的无参构造");
}
}
public class Teacher extends Person{
public Teacher() {
System.out.println("我是Teacher的无参构造");
}
}
//Teacher类继承Person类
public static void main(String[] args) {
new Teacher();
}
//打印结果为:
我是人类中的无参构造
我是Teacher的无参构造
super调用父类有参构造
super():表示调用父类无参构造方法。
super(实参):表示调用父类有参构造方法。
this或super使用在构造方法中时,都要求在首行。
super的用法
- 第一种用法:
- 在子类的方法中使用“super.”的形式访问父类的属性和方法。
- 例如: super.父类属性、super.父类方法();
- 第二种用法:
- 在子类的构造方法的首行,使用“super()”或“super(实参)”,调用父类构造方法。
1.如果子类构造方法中,没有显示定义super()或super(实参),则默认提供super)。
2.同一个子类构造方法中,super().this()不可同时存在。
多态
概念:
父类引用指向子类对象,从而产生多种形态。
- 二者具有直接或间接的继承关系时,父类引用可指向子类对象,即形成多态。
- 父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。
多态的应用
- 场景一:
- 使用父类作为方法形参实现多态,使方法参数的类型更为宽泛。
- 场景二:
- 使用父类作为方法返回值实现多态,使方法可以返回不同子类对象。
向上转型
父类引用中保存真实子类对象成为向上转型。
Animal animal= new Dog();
仅可调用Animal中所声明的属性和方法。
向下转型
将父类引用中的真实子类对象,强转回子类本身类型,称为向下转型。
Animal animal = new Dog();
Dog dog = (dog) animal;
只有转换回子类真实类型,才可调用子类独有的属性和方法。
instanceof关键字
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
Animal animal = new Dog();
if(animal instanceof Dog){
Dog dog = (dog) animal;
}
当“animal”引用中存储的对象类型确实为Dog时,再进行类型转换,进而调用Dog中的独有方法。(避免类转换异常)
多态的应用场景
- 使用父类作为方法形参,实现多态。
- 调用方法时,可传递的实参类型包括:本类型对象+其所有的子类对象。
- 使用父类作为方法返回值,实现多态。
- 调用方法后,可得到的结果类型包括:本类型对象+其所有的子类对象。
多态的作用
- 屏蔽子类间的差异。
- 灵活、耦合度低。
abstract
被abstract修饰的类,称为抽象类。抽象类意为不够完整的类、不够具体的类,抽象类对象无法独立存在,即不能new对象,抽象方法没有方法体,只能被继承实现
作用:
1.可被子类继承,提供共性属性和方法。
2.可声明为引用,更自然的使用多态。
抽象方法
被abstract修饰的方法,称为抽象方法。
抽象方法没有方法体。抽象方法必须存在抽象类中。
产生继承关系后,子类必须重写父类中所有的抽象方法,否则子类还是抽象类
static
- 静态(static)可以修饰属性和方法。
- 称为静态属性(类属性)、静态方法(类方法)。
- 静态成员是全类所有对象共享的成员。
- 在全类中只有一份,不因创建多个对象而产生多份。
- 不必创建对象,可直接通过类名访问。
public class Student {
static int age;
}
静态属性是整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
静态的特点
- 静态方法允许直接访问静态成员。
- 静态方法不能直接访问非静态成员。
- 静态方法中不允许使用this或是super关键字。
- 静态方法可以继承,不能重写、没有多态。
动态代码块
类加载
- JVM首次使用某个类时,需通过CLASSPATH查找该类的.class文件。
- 将.class文件中对类的描述信息加载到内存中,进行保存。
- 如:包名、类名、父类、属性、方法、构造方法..
- 加载时机:
- 创建对象。
- 创建子类对象。
- 访问静态属性。
- 调用静态方法。
- 主动加载:Class.forName(“全限定名”);
静态代码块
对象创建过程
带有继承的对象创建过程
总结:
- static修饰的成员为静态成员,无需创建对象,可直接通过类名访问。
- 静态方法不能直接访问非静态成员。
- 静态方法中不能使用this或super。
- 静态方法可以继承、不能重写、没有多态。
- 静态代码块在类加载时被执行,且只执行一次。
final
最后的,不可被修改的。
-
final可以修饰的内容
- 类(最终类)
- 方法(最终方法)
- 变量(最终变量)
-
final修饰类:此类不能被继承。
- String、Math、System均为final修饰的类,不能被继承。
-
final修饰方法:此方法不能被覆盖。
- 意为最终方法,不支持子类以覆盖的形式修改。
-
final修饰变量:此变量值不可被改变(常量)
- 所有final修饰的变量,只能被赋值一次,值不允许改变
实例常量
class Student{
final String name;
}
实例常量不再提供默认值,必须手动赋予初始值。赋值时机:显示初始化、动态代码块、构造方法。
注意:如果在构造方法中为实例常量赋值,必须保证所有的构造方法都能对其正确赋值。
静态常量
class Student{
static final String name;
}
静态常量不再提供默认值,必须在声明后手动赋值
对象常量
final修饰基本数据类型,值不可变。
final修饰引用数据类型,地址不可变
总结:
- final修饰类:此类不能被继承。
- final修饰方法:此方法不能被覆盖。
- final修饰变量:此变量值不能被改变。(无初始值、只允许赋值一次)
- 局部常量:显示初始化。
- 实例常量:显示初始化、动态代码块、构造方法。
- 静态常量:显示初始化、静态代码块。
- 基本类型常量:值不可变。
- 引用类型常量:地址不可变。
接口
接口是一种标准,一种能力和约定
接口支持多实现,可为类扩充多种能力
接口的语法
与抽象类的异同
- 相同:
- 可编译成字节码文件。
- 不能创建对象。
- 可以作为引用类型。
- 具备Object类中所定义的方法。
- 不同:
- 所有属性都是公开静态常量,隐式使用public static final修饰。
- 所有方法都是公开抽象方法,隐式使用public abstract修饰。
- 没有构造方法、动态代码块、静态代码块。
接口的规范
- 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
- 实现接口中的抽象方法时,访问修饰符必须是public。
接口的引用
同父类一样,接口也可声明为引用,并指向实现类对象
- 注意:
- 仅可调用接口中所声明的方法,不可调用实现类中独有的方法。
- 可强转回实现类本身类型,进行独有方法调用。
接口的动态
常见关系
-
类与类:
- 单继承
- extends 父类名称
-
类与接口:
- 多实现
- implements接口名称1,接口名称2,接口名称n
-
接口与接口:
-
多继承
- extends父接口1,父接口2,父接口n
回调原理:
接口的好处
- 程序的耦合度降低。
- 更自然的使用多态。
- 设计与实现完全分离。
- 更容易搭建程序框架。
- 更容易更换具体实现。
总结:
- 什么是接口:
- 微观:接口是—种能力和约定。
- 宏观:接口是—种标准。
- 接口与类的异同:
- 没有构造方法,仅可定义公开静态常量与公开抽象方法。
- 接口的应用
- Java为单继承,当父类的方法种类无法满足子类需求时,可实现接口扩充子类能力。
- 接口的规范
- 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
- 实现接口中的抽象方法时,访问修饰符必须是public.
- 什么是常量接口
- 将多个常用于表示状态或固定值的变量,以静态常量的形式定义在接口中统一管理。
- 什么是接口回调
- 先有接口的使用者,后有接口的实现者。
内部类
在一个类的内部,再定一个完整的类
特点:
- 编译之后可生成独立的字节码文件。
- 内部类可直接访问外部类的私有成员,而不破坏封装。
- 可为外部类提供必要的内部功能组件。
成员内部类
- 在类的内部定义,与实例变量、实例方法同级别的类。
- 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。
- Outer out = new Outer();
- Outer.Inner in = out.new Inner();
- 当外部类、内部类存在重名属性时,会优先访问内部类属性。
- 成员内部类不能定义静态成员。
静态内部类
- 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。
- 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)。
- Outer.Inner inner = new Outer.lnner();
- Outer.Inner.show();
局部内部类
- 定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。
- 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final。
- 限制类的使用范围。
匿名内部类
- 没有类名的局部内部类(━切特征都与局部内部类相同)。
- 必须继承一个父类或者实现一个接口。
- 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。
- 优点:减少代码量。
- 缺点:可读性较差。
Object
- 超类、基类,所有类的直接或间接父类,位于继承树的最顶层。
- 任何类,如没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承。
- Object类中所定义的方法,是所有对象都具备的方法。
- Object类型可以存储任何对象。
- 作为参数,可接受任何对象。
- 作为返回值,可返回任何对象。
getClass()方法
- public final Class<?> getClass(){}
- 返回引用中存储的实际对象类型。
- 应用:通常用于判断两个引用中实际存储对象类型是否一致。
hashCode()方法
- public int hashCode(){}
- 返回该对象的十进制的哈希码值。
- 哈希算法根据对象的地址或字符串或数字计算出来的int类型的数值。
- 哈希码并不唯一,可保证相同对象返回相同哈希码,尽量保证不同对象返回不同哈希码。
toString()方法
- public String toString(){}
- 返回该对象的字符串表示(表现形式)。
- 可以根据程序需求覆盖该方法,如:展示对象各个属性值。
equals()方法
- public boolean equals(Object obj)0
- 默认实现为(this == obj),比较两个对象地址是否相同。
- 可进行覆盖,比较两个对象的内容是否相同。
equals方法覆盖的步骤
- 比较两个引用是否指向同一个对象。
- 判断obj是否为null。
- 判断两个引用指向的实际对象类型是否一致。
- 强制类型转换。
- 依次比较各个属性值是否相同。
fianlize()方法
- 当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
- 垃圾对象:没有有效引用指向此对象时,为垃圾对象。
- 垃圾回收:由GC销毁垃圾对象,释放数据存储空间。
- 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象。
- 手动回收机制:使用System.gc();通知JVM执行垃圾回收。
面试题
重写与重载的区别:
重写:
- 方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
- 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
重载:
- 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载
this与super的区别:
- super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。
- super()和this()均需放在构造方法内第一行。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
抽象类:
- abstract修饰类:不能new对象,但可以声明引用。
- abstract修饰方法:只有方法声明,没有方法实现。(需包含在抽象类中)
- 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类。
- 子类继承抽象类后,必须重写父类中所有的抽象方法,否则子类还是抽象类。
抽象类与接口的区别:
- 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
- 抽象类要被子类继承,接口要被类实现。
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
- 抽象类里可以没有抽象方法。
- 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
- 接口中没有 this 指针,没有构造函数,不能拥有实例字段(实例变量)或实例方法。
- 抽象类不能在Java 8 的 lambda 表达式中使用。
Q.E.D.