封装

定义:隐藏对象的内部实现细节,控制对象访问与修改

封装的优点

  1. 良好的封装能够减少耦合。
  2. 类内部的结构可以自由修改。
  3. 可以对成员变量进行更精确的控制。
  4. 隐藏信息,实现细节。

过滤有效数据

	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修饰的属性和方法:
    • 访问修饰符的一种,仅同包可见。(详见下图)

image.png

方法的重写

  • 方法重写的规则:
    • 方法名称、参数列表、返回值类型必须与父类相同。
    • 访问修饰符可与父类相同或是比父类更宽泛。
  • 方法重写的执行:
    • 子类重写父类方法后,调用时优先执行子类重写后的方法。

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调用父类有参构造

image.png
super():表示调用父类无参构造方法。
super(实参):表示调用父类有参构造方法。

this或super使用在构造方法中时,都要求在首行。

super的用法

  • 第一种用法:
    • 在子类的方法中使用“super.”的形式访问父类的属性和方法。
    • 例如: super.父类属性、super.父类方法();
  • 第二种用法:
    • 在子类的构造方法的首行,使用“super()”或“super(实参)”,调用父类构造方法。
注意:
1.如果子类构造方法中,没有显示定义super()或super(实参),则默认提供super)。
2.同一个子类构造方法中,super().this()不可同时存在。

多态

概念:

父类引用指向子类对象,从而产生多种形态。

image.png

  • 二者具有直接或间接的继承关系时,父类引用可指向子类对象,即形成多态。
  • 父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。

多态的应用

  • 场景一:
    • 使用父类作为方法形参实现多态,使方法参数的类型更为宽泛。
  • 场景二:
    • 使用父类作为方法返回值实现多态,使方法可以返回不同子类对象。

向上转型

父类引用中保存真实子类对象成为向上转型。

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关键字。
  • 静态方法可以继承,不能重写、没有多态。

动态代码块

image.png

类加载

  • JVM首次使用某个类时,需通过CLASSPATH查找该类的.class文件。
  • 将.class文件中对类的描述信息加载到内存中,进行保存。
    • 如:包名、类名、父类、属性、方法、构造方法..
  • 加载时机:
    • 创建对象。
    • 创建子类对象。
    • 访问静态属性。
    • 调用静态方法。
    • 主动加载:Class.forName(“全限定名”);

静态代码块

image.png

对象创建过程

image.png

带有继承的对象创建过程

image.png

总结:

  • 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修饰变量:此变量值不能被改变。(无初始值、只允许赋值一次)
    • 局部常量:显示初始化。
    • 实例常量:显示初始化、动态代码块、构造方法。
    • 静态常量:显示初始化、静态代码块。
    • 基本类型常量:值不可变。
    • 引用类型常量:地址不可变。

接口

接口是一种标准,一种能力和约定
接口支持多实现,可为类扩充多种能力

接口的语法

image.png

与抽象类的异同

  • 相同:
    • 可编译成字节码文件。
    • 不能创建对象。
    • 可以作为引用类型。
    • 具备Object类中所定义的方法。
  • 不同:
    • 所有属性都是公开静态常量,隐式使用public static final修饰。
    • 所有方法都是公开抽象方法,隐式使用public abstract修饰。
    • 没有构造方法、动态代码块、静态代码块。

接口的规范

  • 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
  • 实现接口中的抽象方法时,访问修饰符必须是public。

接口的引用

同父类一样,接口也可声明为引用,并指向实现类对象

  • 注意:
    • 仅可调用接口中所声明的方法,不可调用实现类中独有的方法。
    • 可强转回实现类本身类型,进行独有方法调用。

接口的动态

image.png
image.png

常见关系

  • 类与类:

    • 单继承
    • extends 父类名称
  • 类与接口:

    • 多实现
    • implements接口名称1,接口名称2,接口名称n
  • 接口与接口:

  • 多继承

    • extends父接口1,父接口2,父接口n

回调原理:
image.png

接口的好处

  • 程序的耦合度降低。
  • 更自然的使用多态。
  • 设计与实现完全分离。
  • 更容易搭建程序框架。
  • 更容易更换具体实现。

总结:

  • 什么是接口:
    • 微观:接口是—种能力和约定。
    • 宏观:接口是—种标准。
  • 接口与类的异同:
    • 没有构造方法,仅可定义公开静态常量与公开抽象方法。
  • 接口的应用
    • Java为单继承,当父类的方法种类无法满足子类需求时,可实现接口扩充子类能力。
  • 接口的规范
    • 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
    • 实现接口中的抽象方法时,访问修饰符必须是public.
  • 什么是常量接口
    • 将多个常用于表示状态或固定值的变量,以静态常量的形式定义在接口中统一管理。
  • 什么是接口回调
    • 先有接口的使用者,后有接口的实现者。

内部类

在一个类的内部,再定一个完整的类
特点:

  • 编译之后可生成独立的字节码文件。
  • 内部类可直接访问外部类的私有成员,而不破坏封装。
  • 可为外部类提供必要的内部功能组件。
    image.png

成员内部类

  • 在类的内部定义,与实例变量、实例方法同级别的类。
  • 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。
    • 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.


如人饮水、冷暖自知