面向对象
- 类的定义
- 构造方法
-
- 什么是构造方法
- 默认无参构造
- 构造方法的语法结构
- 调用构造方法
- 封装
-
- static关键字
- 静态代码块
-
- 实例代码块
-
- 静态方法调用实例成员
- this关键字
- super关键字
- 继承
-
- 方法重写
-
- 重写的条件
- 多态
-
- 向上转型和向下转型
-
- 什么时候需要向下转型?
- 向下转型的风险
- 如何避免这个风险?
- 什么是多态?
- 多态在开发中的作用
- final 关键字
- 静态类型、动态类型、静态绑定、动态绑定
- 方法覆盖、方法隐藏
- 类的初始化过程
- 实例的初始化过程
- 抽象类
-
- 什么是抽象类?
- 抽象类属于什么类型?
- 定义语法
- 特性
- 抽象方法
-
- 接口
-
- 类和类之间的关系
- 抽象类和接口的区别
类的定义
[修饰符列表] class 类名 {
//类体 = 属性 + 方法
// 属性在代码上以“成员变量”的形式存在(描述状态)
// 方法描述动作/行为
}
注意:
- 修饰符列表可以省略;
- 实例化对象,new 类名();
- 变量必须先声明再赋值才能访问,成员变量没有手动赋值时,系统默认赋值。
构造方法
什么是构造方法
构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。换句话说:构造方法是用来创建对象,并且同时给对象的属性赋值。
默认无参构造
当一个类没有提供任何构造方法时,系统会默认提供一个无参数的构造方法。(而这个构造方法被称为缺省构造器)
构造方法的语法结构
[修饰符列表] 类名(形式参数列表){
构造方法体
通常在构造方法体当中给属性赋值,完成属性的初始化。
}
注意:
- 修饰符列表目前统一写:public。千万不要写public static,单例模式–private;
- 构造方法名和类名必须一致;
- 构造方法不需要指定返回值类型,也不能写void;
- 构造方法支持方法重载。
调用构造方法
new 构造方法名(实际参数列表);
封装
封装的作用
- 保证内部结构的安全性;
- 屏蔽复杂,暴露简单。
从代码的角度上看,一个类体当中的数据,封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,只需要通过一个简单的入口就可以访问。另外,类体中安全级别较高的数据封装起来,外部人员不能随意访问,保证数据的安全性。
实现封装
- 属性私有化(使用private关键字进行修饰);
- 对外提供简单的操作入口,通过set方法修改,get方法读取。
static关键字
- 所用static关键字修饰的都是类相关的,类级别的;
- 所用static修饰的,都是采用“类名.”的方式访问;
- static修饰的变量:静态变量;
- static修饰的方法:静态方法。
静态代码块
语法格式
static {
java语句;
java语句
}
执行时机
类加载时执行,在main方法执行前执行,且只执行一次。
作用
- 静态代码块不是那么常用;
- 是sun公司给我们java程序员的一个特殊的时刻/时机 – 类加载时机。
实例代码块
语法格式
{
java语句;
java语句;
java语句;
}
执行时机
只要是构造方法执行,必然构造方法执行之前,自动执行“实例语句快”中的代码。实际上这也是SUN公司为java程序员准备的一个特殊时机,叫做对象构建时机。
静态方法调用实例成员
不能!!!
- 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
- 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。
注意:静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。
this关键字
- 一个对象一个this。this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。
- this存储在堆内存当中的对象内部。
- this只能使用在实例方法中。谁调用这个实例方法,this就是谁。构造函数内,this指向当前对象。
- this大部分情况下是可以省略的。在实例方法中,或构造方法中,为了区分局部变量和实例变量,"this."是不能省略的。
- 新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:
this(实际参数列表);
this()的调用只能出现在构造方法的第一行。
super关键字
- super能出现在实例方法和构造方法中;
- super的语法是:
super.
、super()
;
- super不能使用在静态方法中;
- super.大部分情况下是可以省略的;
- super.在区分局部变量和实例变量的时候不能省略;
- super() 只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类特征。
注意:
- this()和super() 不能共存,它们都是只能出现在构造方法的第一行;
- 当一个构造方法的第一行,既没有this()又没有super()的话,默认会有一个super();
- 当子类对象中的出现和父类同名的属性时,为了在子类对象中访问“父类特征”super. 不能省略;
- super 不是引用,也不保存内存地址,也不指向任何对象。super只代表当前对象
内部的那块父类型特征。super使用时后面必须跟“点”,即 super.
。
调用格式:
- super.属性名 【访问父类的属性】
- super.方法名 【访问父类的方法】
- super(实参) 【调用父类的构造方法】
继承
继承的作用
- 基本作用:子类继承父类,代码可以得到复用;
- 主要作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。
继承的特性
- B类继承A类,则称A类为超类(superclass)、父类、基类,
B类则称为子类(subclass)、派生类、扩展类。
class A{}
class B extends A{}
- java中的继承只支持单继承,不支持多继承,C++中支持多继承,
这也是java体现简单性的一点,换句话说,java中不允许这样写代码:
class B extends A,C{ }。
- 虽然java中不支持多继承,但有的时候会产生间接继承的效果,
例如:class C extends B,class B extends A,也就是说,C直接继承B,
其实C还间接继承A。
- java中规定,子类继承父类,除构造方法不能继承外,剩下都可以继承。
但是私有的属性无法在子类中直接访问。
- java中的类没有显示的继承任何类,则默认继承Object类,Object类是
java语言提供的根类(超级父类),也就是说,一个对象与生俱来就有
Object类型中所有的特征。
- 继承也存在一些缺点,例如会造成子类与父类耦合度高的问题。
方法重写
重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
什么时候重写?
子类继承父类后,当继承过来的方法无法满足当前子类的业务需求时,子类有权力对这个方法进行重新编写,有必要进行“方法的覆盖”。
注意: 当子类对父类继承过来的方法进行“方法重写”之后,子类对象调用该方法的时候,一定执行重写后的方法。
重写的条件
- 两个类必须要有继承关系;
- 方法名、参数列表必须相同;
- 子类方法返回值类型应比父类方法返回值类型更小或相等;
- 抛出的异常范围小于等于父类;
- 访问修饰符范围大于等于父类
注意:
- 构造方法无法继承,故无法被重写;
- 被
private/final/static
修饰的方法无法被重写,但是被 static
修饰的方法可以被再次声明。
多态
向上转型和向下转型
向上转型:子—>父(upcasting), 又被称为自动类型转换:Animal a = new Cat();
向下转型:父—>子(downcasting),又被称为强制类型转换:Cat c = (Cat)a;
什么时候需要向下转型?
需要调用或执行子类对象特有的方法,必须向下转型才可以调用。
向下转型的风险
容易出现ClassCastException(类型转换异常)
如何避免这个风险?
instanceof
运算符,可以在程序运行阶段动态的判断某个引用指向的对象是否为某一种类型。
什么是多态?
多种形态、多种状态,编译和运行有两个不同的状态。
Animal a = new Cat();
a.move();
多态的典型代码:父类引用指向子类对象。
多态在开发中的作用
降低程序的耦合度,提高程序的扩展力。
final 关键字
final表示不可改变的含义,
- final修饰的类不能被继承;
- final修饰的方法不能被重写;
- final修饰的变量不能被修改;
- final修饰的变量必须显示初始化,final修饰的实例变量一般添加static修饰,static final联合修饰的变量称为“常量”;
- final修饰引用只能固定指向一个对象,也就是说这个引用不能再次赋值,但被指向的对象是可以修改的;
- 构造方法不能被final修饰;
- 会影响 Java 类的初始化:final定义的静态常量调用时不会执行java的类初始化方法。
静态类型、动态类型、静态绑定、动态绑定
任何一个引用变量都有两个类型:
- 静态类型,也就是定义该引用变量的类型;
- 动态类型,也就是该引用实际指向的对象类型。
比如对于两个类A和类B,有:A a=new B();
那么,引用a的静态类型就是A,动态类型就是B。
静态绑定:所有依赖于静态类型来将某方法和该方法所在的类关联起来的动作都是静态绑定。因为静态绑定在程序运行前发生,所有又叫前期绑定。
动态绑定:所有依赖于动态类型来将某方法和该方法所在的类关联起来的动作都是动态绑定。因为动态绑定是在程序运行时,通过RTTI实现,所以又叫后期绑定。
注意:java中 类的属性(变量) 都是静态绑定的。
方法覆盖、方法隐藏
- 所有的成员变量(不管是静态还是非静态)都只进行静态绑定;
- 对于静态方法,也是只进行静态绑定;
- 对于非静态方法,会进行动态绑定。
方法隐藏: 对于1和2这两种情况,子类继承父类后,父类的属性和静态方法并没有被子类抹去, 通过相应的引用可以访问的到。但是在子类中不能显示地看到,这种情况就称为隐藏。
方法覆盖: 而对于3这种情况,子类继承父类后,父类的非静态方法被子类重写后覆盖上去,通过相应的引用也访问不到了(除非创建父类的对象来调用)。这种情况称为覆盖。
类的初始化过程
- 一个类要创建实例需要先加载并初始化该类 – main方法所在的类需要先加载和初始化;
- 一个子类要初始化需要先初始化父类;
- 一个类的初始化就是执行()方法 – ()方法是自动生成的,在字节码中可以找到,是由 静态类变量显示赋值代码 和 静态代码块 组成,这两个是谁在上面先执行谁,并且只执行一次。
实例的初始化过程
- 实例的初始化就是执行()方法 – () 方法可能有多个重载,有几个构造方法就有几个() ;
- () 方法包括 非静态类实例变量显示赋值代码 , 非静态代码块 和 对应构造器代码 组成,前两个是谁在上面先执行谁,构造器代码最后执行 ;
- 每次创建实例都要调用对应的()方法,有因为子类的构造器的首行是super();所以每次都会调用父类的()方法。
抽象类
什么是抽象类?
类和类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。
抽象类属于什么类型?
引用数据类型
定义语法
[修饰符列表] abstract class 类名{
类体;
}
特性
- 抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的;
- final和abstract不能联合使用,这两个关键字是对立的,因为final修饰的类无法被继承;
- 抽象类的子类可以是抽象类,也可以是非抽象类;
- 抽象类无法实例化,但是抽象类有构造方法,这个构造方法供子类调用;
- 抽象类中不一定有抽象方法,但抽象方法必须出现在抽象类中;
- 一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行重写、覆盖,实现。
抽象方法
抽象方法表示没有实现的方法,没有方法体的方法。
定义格式
public abstract void doSome();
特点
- 没有方法体,以分号结尾;
- 修饰符列表中有abstract关键字。
接口
- 接口是一种“引用数据类型”;
- 接口是完全抽象的;
- 接口支持多继承;
- 接口中只有常量+抽象方法;
- 接口中所有的元素都是
public
修饰的;
- 接口中抽象方法的
public abstract
可以省略;
- 接口中常量的
public static final
可以省略;
- 接口中的方法不能有方法体;
- 一个非抽象的类,实现接口的时候,必须将接口中的所有方法加以实现;
- 一个类可以实现多个接口;
- extends和implement可以共存,extends在前,implement在后;
- 使用接口,写代码的时候,可以使用多态(父类型引用指向子类型对象)。
定义格式
[修饰符列表] interface 接口名{
常量;
抽象方法;
}
接口在开发中的作用
面向接口编程,可以降低程序的耦合度,提高程序的扩展力,符合OCP开发原则。接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度)
接口可以解耦合,任何一个接口都有调用者和实现者。接口可以将调用者和实现者解耦合。调用者面向接口调用,实现者面向接口编写实现。
类和类之间的关系
is a(继承):
凡是能够满足is a的表示“继承关系“
A extends B
has a(关联):
凡是能够满足has a关系的表示“关联关系”
关联关系通常以“属性”的形式存在。
A{
B b
}
like a(实现):
凡是能够满足like a关系的表示“实现关系”
实现关系通常是:类实现接口。
A implements B
抽象类和接口的区别
-
抽象类是半抽象的,接口是完全抽象的。
-
抽象类中有构造方法,接口中没有构造方法。
-
接口之间支持多继承,类之间只能单继承。
-
一个类可以同时实现多个接口,一个抽象类只能继承一个类。
-
接口中只允许出现常量和抽象方法。