继承
当子类继承父类,可以使子类拥有和父类相同的属性和相同的行为
继承的好处(学习目的)
1.学习继承是学习"实现"的前提条件
2.学习继承是学习"多态"的前提条件之一
3.提高代码的复用性,从而提高开发效率
4.提高代码的扩展性
继承的格式
public class 父类类名 {}
public class 子类类名 extends 父类类名 {}
继承的特点
1.子类拥有和父类相同的属性和相同的行为
2.在程序中一个子类只能拥有一个直接父类(单继承),一个父类可以拥有多个直接子类
3.Java中虽然不支持多继承,但是支持多层继承
4.在程序中父类和子类是一对相对的概念,例如C类继承B类,B类继承A类
A类是B类的直接父类,A类是C类的间接父类,B类是C类的直接父类
C类是B类的直接子类,C类是A类的间接子类,B类是A类的直接子类
5.在Java中所有类都隐式继承一个类,这个类就是Java中顶级父类Object
在继承关系中各成员和内容的特点
私有成员
静态成员
实例变量
实例方法
构造器
在继承关系中私有成员的特点
官方结论:
1.子类可以继承父类中所有的成员
2.子类可以继承同包下父类的私有成员
3.子类不可以继承父类的私有成员
自己总结:
子类可以继承父类的私有成员,但受限于private关键字的特点,
无法直接进行方法,如果想进行访问,需要借助公共获取方式
继承关系中静态成员的特点
子类可以继承父类的静态成员
继承关系中实例变量的特点
子类可以继承父类中的实例变量
this和super关键字
this关键字的第一种用法:
场景:
子类的构造器中或子类的实例方法中
格式:
this.实例变量名;
this.实例方法名(实参);
作用:
用来区分同一个类中同名的实例变量和局部变量
含义:
哪个对象调用了this关键字所在的构造器或实例方法,this关键字就代表哪个对象
super关键字的第一种用法:
场景:
子类的构造器中或子类的实例方法中
格式:
super.实例变量名;
super.实例方法名(实参);
作用:
用来区分子父类继承关系中同名的实例变量
用来区分子父类继承关系中同名的实例方法
含义:
哪个对象调用了super关键字所在的构造器或实例方法,super关键字就代表哪个对象的父类引用
//子类的实例变量
int num = 20;
public void method () {
//子类的局部变量
int num = 30;
System.out.println("num = " + num);//30 就近原则
System.out.println("num = " + this.num);//20
System.out.println("num = " + super.num);//10 父类的实例变量
}
在继承关系中实例方法的特点
子类可以继承父类的实例方法
继承关系中构造器的特点
1.在初始化子类子类成员前先初始化父类成员,主要是由子类构造器中第一部分的内容进行加载父类的构造器
2.当一个类的构造器中没有任何this(实参)或super(实参)时,JVM会自动分配一个super(),用来初始化子类实例成员前先初始化父类引用成员,反之不会进行分配
3.父类的构造器子类不可以继承
方法的重写:
含义:
在继承关系中(或实现关系中),出现了方法名相同,形参列表相同,权限访问级别和返回类型遵循相关规则的现象
目的:
为了后期进行功能的扩展,并且保留原有的功能,需要将父类的方法进行重写
//创建子类对象
SubClass sc = new SubClass();
//调用method()
sc.method();
//父类
public void method () {
System.out.println("父类的实例方法");
}
public void message () {
System.out.println("文字短信");
}
//子类
public void method () {
System.out.println("子类的实例方法");
//return 10;
}
public void message () {
System.out.println("文字短信");
System.out.println("图片彩信");
System.out.println("......");
}
方法重写的前提条件(必须满足以下全部的条件)
1.必须要有继承关系或者实现关系
2.重写方法的方法名必须和被重写方法的方法名相同
3.重写方法的形参列表必须和被重写方法的形参列表相同
4.重写方法的权限访问级别必须大于等于被重写方法的权限访问级别,并且遵循方法重写有关权限访问级别的注意事项
(详见方法重写的注意事项)
5.在内存中,重写方法的返回类型必须和被重写方法的返回类型相同
回顾方法重载的前提条件(必须满足以下全部的条件)
1.必须在同一个类中或者子父类继承关系中
2.方法名必须相同
3.形参列表必须不同(至少满足以下一点)
(1)形参的个数不同
(2)形参的数据类型不同
(3)形参的数据类型顺序不同
方法重写的注意事项
1.在程序中有些时候即使不满足方法的重写规则,代码也不会编译报错进行提示,例如:继承关系中的方法重载;Java中提供了一种特殊
的语法规则--注解
注解
含义:
Java中提供用来检测一些特殊语法格式的特定接口,其中就包含一个用来检测子类方法是否满足重写规则的注解
位置:
声明在子类重写方法上
格式:
@Override
修饰符 返回类型 子类重写方法的方法名 () {}
好处:
将不符合Java重写语法规则的情况提前到编译期
2.父类的私有方法不可以被子类进行重写
3.不同包下父类的缺省方法不可以被子进行重写
4.父类的静态方法不可以被子类进行重写,在子类中编写和父类同名且同形参列表的方法时,也需要遵循方法重写的语法规则
(往往出现在面试过程中的笔试题)
5.在代码中,
当父类被重写方法的返回类型是void时,子类重写父类该方法时返回类型也必须是void
当父类被重写方法的返回类型是基本类型时,子类重写父类该方法时返回类型也必须是该基本类型
当父类被重写方法的返回类型是引用类型时,子类重写父类该方法时返回类型可以是该类型本身,也可以是该类型的子类类型;
(其实就是即将要讲解的面向对象第三大特征多态)
this关键字和super关键字的第二种用法
场景:
子类的构造器中
格式:
this(实参);
作用:
调用本类中其它的构造器
含义:
当构造器没有进行实例初始化时,通过this(实参)调用本类中其它构造器完成了实例初始化
super关键字的第二种用法:
场景:
子类的构造器中
格式:
super(实参);
作用:
调用父类对应参数的构造器
含义:
在初始化子类实例成员前,先通过super()调用父类构造器完成父类的成员初始化操作
this关键字和super关键字的注意事项:
1.在静态成员中不可以使用this关键字和super关键字
2.在创建继承关系中的子类对象时,在子类的构造器中或实例方法中可以正常打印this关键字,但不可以打印super关键字
3.在构造器中this(实参)或super(实参)必须编写在第一行
4.在构造器中this(实参)或super(实参)不可以同时使用
猫鼠案例
//animal类
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//抽取子类们相同且需要子类们进行重写的方法
public void eat () {
System.out.println("吃东东");
}
//抽取子类们相同且不需要子类们进行重写的方法
public void sleep() {
System.out.println("睡觉觉");
}
//cat类
//重写父类中的eat()
@Override
public void eat () {
System.out.println(getAge() + "岁的" + getName() + "正在吃鱼");
}
//猫有特有方法
public void catchMouse () {
System.out.println(getAge() + "岁的" + getName() + "正在抓老鼠");
}
public Cat() {
super();
}
public Cat(String name, int age) {
super(name, age);
}
//dog类
//重写父类中的eat()
@Override
public void eat () {
System.out.println(getAge() + "岁的" + getName() + "正在吃骨头");
}
//狗有特有方法
public void lookHome () {
System.out.println(getAge() + "岁的" + getName() + "正在看家");
}
public Dog() {
super();
}
public Dog(String name, int age) {
super(name, age);
}
//main方法
//创建猫对象
Cat cat = new Cat("Tom", 2);
cat.catchMouse();
cat.eat();
cat.sleep();
System.out.println("================================");
//创建狗对象
Dog dog = new Dog("TwoHa", 3);
dog.lookHome();
dog.eat();
dog.sleep();
Object类
类的特点
1.Object类是类层次的顶级父类
2.所有的对象(包括数组)都可以调用Object类中的方法
3.在所有的接口中都隐式含有Object类中方法的抽象形式(暂不涉及)
类的位置
java.lang
类的构造器
public Object()
该构造器的作用不是用来创建Object类的对象的,而是创建Object类子类的对象时,
在初始化子类成员之前通过super()调用Object类的构造器,完成父类成员的初始化工作
类的方法
public boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。
public String toString()
返回该对象的字符串表示。
==和equals()的区别:
==既可以比较基本类型的数据,也可以比较引用类型的数据
当比较基本类型的数据时,比较的是基本类型的数据值是否相等
当比较引用类型的数据时,比较的是引用类型的地址值是否相等
equals()只能比较引用类型的地址值是否相等,不能比较基本类型
当比较引用类型的数据时,比较的是引用类型的地址值是否相等
当比较的引用类型重写Object类中的equals(),需要根据重写后的规则进行比较
特殊的实例方法toString()
在使用输出语句打印对象名(包含数组)时,其实就是在隐式调用toString(),打印char[]数组除外
//创建2个学生对象
Student s1 = new Student();
Student s2 = new Student();
System.out.println(s1);
System.out.println(s2);
//比较两个对象地址值是否相等
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);
//创建学生对象
Student s = new Student();
System.out.println(s.toString());
System.out.println(s);
//创建int类型数组
int[] ints = {11,22,33};
System.out.println(ints.toString());
System.out.println(ints);
//创建char类型数组
char[] chars = {'a','b','c'};
System.out.println(chars.toString());
System.out.println(chars); //abc
权限访问级别
封装思想的核心就是权限访问级别,给不同的内容添加不同的权限访问级别
权限访问级别从小到大的顺序是:
private < 缺省 < protected < public
权限访问级别修饰内容的范围:
private级别:
修饰范围:
被private修饰的内容只能在本类中有效,在本类之外无法进行访问
"缺省"级别:
修饰范围:
兼容private级别的作用范围
"缺省"级别的内容在本包(同一个包)中有效,在本包(同一个包)之外位无法进行访问
protected级别:
修饰范围:
兼容"缺省"级别的作用范围
protected级别修饰的内容在同一个项目且有继承关系时有效
如果同一个模块中,可以直接使用
如果不在同一个模块中,修改项目的配置文件,无需手动修改可以使用alt+enter
public级别:
修饰范围:
兼容"protected"级别的作用范围
public级别修饰的内容在同一个项目有效
如果同一个模块中,可以直接使用
如果不在同一个模块中,修改项目的配置文件,无需手动修改可以使用alt+enter
权限访问级别的补充:
类(class,interface,enum)文件:
权限级别:只能public和缺省,推荐使用public
类中的成员量:
权限级别:四种都可以,推荐使用private
类中的成员方法:
权限级别:四种都可以,推荐使用public(常规方法),protected(复杂继承关系),private(只限自己访问的方法)
类中的成员内部类:
权限级别:四种都可以,推荐使用缺省和private
类中的构造器:
权限级别:四种都可以,推荐使用public(常规)和private(在外界不想创建对象时)
类中的构造器代码块:
权限级别:只能缺省
类中的静态代码块:
权限级别:只能缺省
局部内容:
权限级别:只能缺省
abstract(抽象) 关键字
关键字的由来(继承关系代码中存在的安全隐患)
1.父类是多个子类抽取的共性内容,希望这个类不可以被实例化对象,但是实际上可以创建父类对象
2.父类中抽取多个子类相同且需要重写的方法,最终创建对象调用的方法肯定执行子类重写后的方法,父类声明的方法就显得不是那么重要了,希望在父类中声明这个方法时,省略方法体,但是实际上省略方法体代码编译报错;
3.程序员在重写父类中的方法时,偶尔会出现忘记某个类重写方法,希望代码编译报错进行友情提示,但是实际上没有任何提示
为了解决上述问题,引出面向对象中的"抽象"
abstract关键字:
含义:
抽象的
修饰:
类(class),方法
抽象类:
含义:
被abstract关键字修饰的类,也被称之为"加强版父类"或者"父类plus"
位置:
修饰父类
格式:
public abstract class 父类类名 {}
注意:
1.被abstract关键字修饰的类,不可以被实例化对象
2.抽象类中可以含有构造器,构造器的作用是创建子类对象时,初始化父类成员用的
抽象方法:
含义:
被abstract关键字修饰且没有方法体的方法
位置:
抽象类中
格式:
修饰符 abstract 返回类型 方法名 ();
注意:
1.抽象类可以没有抽象方法,但是含有抽象方法的类一定是抽象类
2.抽象类的子类必须重写抽象父类中所有抽象方法,否则编译报错,除非该子类也是抽象类
猫鼠案列
//animal 类
public abstract class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//抽取子类们中的共性方法,且需要重写
public abstract void eat();
//抽取子类们中的共性方法,且不需要重写
public void sleep () {
System.out.println("睡觉觉");
}
}
//其余的和上面的一样
final关键字
含义:
不可改变的,最终的
修饰:
类,方法,变量
特点:
被final修饰的类,不可以被继承
被final修饰的方法,不可以被重写
被final修饰的变量,不可以被重新赋值,其实就是自定义常量
最终类:
含义:
被final修饰的类
格式:
public final class 类名 {
}
注意:
1.被final修饰的类,不可以被继承
2.abstract关键字和final关键字不可以同时修饰一个类
最终方法:
含义:
被final关键字修饰的成员方法
格式:
修饰符 final 返回类型 方法名 () {}
注意:
1.被final关键字修饰的成员方法不可以被重写
2.abstract关键字和final关键字不可以同时修饰一个方法
自定义常量:
含义:
被final修饰的变量
格式:
修饰符 final 数据类型 常量名 = 初始化值;
分类:
局部常量
实例常量
静态常量
注意:
1.被final修饰的变量不可以被二次赋值,其实就是一个自定义常量
2.常量名的命名规范:
一个单词:
单词全部大写
多个单词:
单词全部大写,且每个单词间使用下划线连接
3.在程序中,JVM会将被final修饰,且通过直接声明初始化的自定义常量当成字面值常量进行使用
实例常量:
含义:
被final关键字修饰的实例变量
注意:
1.实例常量在进行加载的时候,JVM不会给其进行赋默认值操作
2.如果实例常量在成员中进行先声明后初始化,我们需要在所有的构造器中完成实例常量的赋值操作
如果所有构造器中给该实例常量赋的值都相同,可以将该条代码提取到"构造器代码块"中
如果所有构造器中给该实例常量赋的值不相同,只能在每个构造器中进行赋值操作
静态常量:
含义:
被final关键字修饰的静态变量
注意:
1.静态常量在进行加载的时候,JVM不会给其进行赋默认值操作
2.如果静态常量在成员中进行先声明后初始化,我们需要在静态代码块中给该静态常量进行初始化赋值操作
接口
含义:
接口和类都是数据类型
接口是定义一组固定数据和行为的标准
好处:
1.开发过程中的标准(类似于写论文的"大纲"或者"脑图")
2.提高程序的扩展性
3.提高程序的复用性,从而提高开发效率
4.学习接口的"实现关系",是学习多态的前提条件之一
5.接口的实现关系可以避免单继承的局限性
接口的格式
public interface 父接口名 {
内部接口(暂不涉及)
静态常量
抽象方法
默认方法(JDK8.0)
静态方法(JDK8.0)
私有方法(JDK9.0)
}
实现类的格式:
在实现的同时,也具有继承关系:
public class 实现类类名 extends 父类类名 implements 父接口名1,父接口名2,......,父接口名n{}
在实现的同时,没有继承关系:
public class 实现类类名 implements 父接口名1,父接口名2,......,父接口名n{}
接口的继承格式(了解):
public interface 接口名 extends 父接口名1,父接口名2,......,父接口名n{}
接口的注意事项:
1.接口中的成员只有6个:内部接口,静态常量,抽象方法,默认方法,静态方法,私有方法,除外之外没有任何内容
2.接口无法直接创建对象,但是可以通过接口创建实现类对象
实现类:类与接口之间的关系是实现关系(和继承类似,但有些不同),将实现接口的类称之为实现类
3.程序中类文件的关系(类,接口)
类与类之间的关系:
单继承
类与接口之间的关系:
多实现
接口与接口之间的关系:
多继承
接口的使用
1.根据接口创建接口的实现类
2.重写接口中的抽象方法
3.在测试类中创建接口的实现类对象
4.调用实现类对象的方法完成需求
接口中的静态常量
格式:
[public] [static] [final] 数据类型 常量名 = 初始化值
调用:
接口名.静态常量名
注意:
1.接口中声明的静态常量只能直接声明初始化,不可以先声明后初始化
2.[]中的关键字在声明接口中的静态常量时,可以省略不写,如果不写,由JVM隐式补全
3.实现类可以继承接口中的静态常量
接口中的抽象方法:
格式:
[public] [abstract] 返回类型 方法名 ();
注意:
[]中的关键字可以省略不写,如果不写由JVM进行补全
接口的默认方法(JDK8.0):
含义:
其实就是接口实现类的实例方法,为了满足JavaBean设计规范,将实例方法提取到接口中以默认方法形式声明
格式:
[public] default 返回类型 方法名 () {}
注意:
[ ]中public可以省略不写,如果不写由JVM自动补全
猫狗案例(接口版)
//animalInterface 接口类
public interface AnimalInterface {
//设计动物吃功能,需要重写
void eat();
//设计动物睡觉功能,不需要重写
default void sleep() {
System.out.println("睡觉觉");
}
}
//animal 类
public abstract class Animal implements AnimalInterface {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// cat 类
public class Cat extends Animal {
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(getAge() + "岁的" + getName() + "正在吃鱼");
}
//猫的特有方法
public void catchMouse () {
System.out.println(getAge() + "岁的" + getName() + "正在抓老鼠");
}
}
//dog 类
public class Dog extends Animal {
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(getAge() + "岁的" + getName() + "正在吃骨头");
}
//狗的特有方法
public void lookHome () {
System.out.println(getAge() + "岁的" + getName() + "正在看家");
}
}
//main 方法
//创建猫对象
Cat cat = new Cat("Tom", 2);
cat.catchMouse();
cat.eat();
cat.sleep();
System.out.println("===============");
//创建狗对象
Dog dog = new Dog("TwoHa", 3);
dog.lookHome();
dog.eat();
dog.sleep();
默认方法
默认方法学习目的:
1.源码中存在默认方法
2.面试题或笔试题
默认方法的注意事项
1.一个类继承父类同时实现父接口,父类中的实例方法和父接口中的默认方法发生同名的会时候,通过子类对象调用这个同名的方法,
执行的是父类中的实例方法
2.一个类不继承父类,同时实现多个父接口,且多个父接口中存在同名的默认方法,此时子类会编译报错,需要子类重写父接口中的同名
默认方法才可以
接口中的静态方法(JDK8.0)
格式:
[public] static 返回类型 方法名 () {}
[]中关键字可以省略不写,当不写时由JVM自动进行填补
调用:
接口名.静态方法名();
注意:
继承关系和实现关系的区别:
1.在继承关系中子类可以继承父类的静态方法
在实现关系中实现类不可以继承父接口的静态方法
2.继承关系:
如果是类与类之间的关系:只支持单继承
如果是接口与接口之间的关系:支持多继承
实现关系:
类与接口之间的关系:多实现
接口中的私有方法(JDK9.0)
格式:
private 返回类型 方法名 () {}
没有static的私有方法是抽取多个默认方法的共性内容存储在私有方法中
private static 返回类型 方法名 () {}
含有static的私有方法是抽取多个静态方法的共性内容存储在私有方法中