目录
1.继承
1.1 继承概述
继承是面向对象的三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法
继承相关:
- 格式:public class 子类名 extends 父类名{}
- 父类也被称为基类和超类
- 子类也被称为派生类
继承中子类的特点:
- 子类可以有父类的内容,还可以有自己的内容
1.2 继承的好处和弊端
好处:
- 提高代码的复用性(多个类相同的成员可以放到一个类中)
- 提高代码的维护性(如果方法的代码需要修改,修改一次即可)
弊端:
- 类与类之间的耦合性增强了,当父类发生变化时,子类也跟着变化
1.3 super关键字
super关键字与this关键字用法相似,this代表本类对象的使用,super代表父类对象的引用
关键字 | 访问成员变量 | 访问构造方法 | 访问成员方法 |
this | this.成员变量 访问本类中的成员变量 | 访问本来中的构造方法 | this.成员方法() 访问本类中的成员方法 |
super | super.成员变量 访问父类的成员变量 | 访问父类的构造方法 | super.成员方法() 访问父类成员方法 |
1.4 继承中构造方法的访问特点
- 子类中所有的构造方法都会默认先访问父类中的无参构造方法
- 因为子类中会继承或者使用父类中的数据,所以要在子类初始化以前先完成父类初始化
- 每一个子类构造方法第一条语句都是默认super()的无参构造方法
1.5 继承中成员方法的访问特点
通过子类对象访问一个方法
- 先在子类成员范围找,子类没有去父类成员范围找,都没有就报错
1.6 super内存图
a.先在堆内存中开辟新的子类对象的空间,访问zi的成员变量,再访问构造方法到栈内存
b.由于子类构造方法中默认有一个父类的初始化,所以构造时要有一个父类的成员对象被传入堆内存中,且父类的构造方法入栈
c.父类的执行完毕,构造方法出栈,对象z创建完成,zi的构造方法出栈
d.执行子类的show方法,先入栈,输出age(局部变量的age)
输出this.age
输出super.age:首先找到调用者,通过调用者找到super,在输出值,并让show方法出栈
e:如果访问子类没有的方法,就去父类找,父类没有就报错,全部执行完毕,main方法出栈
1.7 方法重写
- 方法重写:子类和父类出现一模一样的方法声明
- 重写的应用:当子类需要父类的功能,而功能主体子类有自己的内容时可以重写方法,这样既可以沿袭父类功能,又有他自身独特的功能
- 用@override检查重写方法的正确性
注意:
- 私有方法不能被重写,私有成员不能被继承
- 子类方法访问权限不能低于父类的(public>默认>私有)
1.8 Java中继承的注意事项
- Java中类只支持单继承,不支持多继承
- Java中类支持多重继承
1.9 修饰符
1.9.1 权限修饰符
修饰符 | 同一个类中 | 同一个包中子类无关类 | 不同包的子类 | 不同包无关类 |
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
1.9.2 状态修饰符
final关键字
常用四种用法
- 修饰类
- 修饰方法
- 修饰局部变量
- 修饰一个成员变量
1.修饰类
- 当final关键字用来修饰一个类时,格式public final class{}
- 含义:当前类不能有子类,故其成员方法都不能被重写
2.修饰方法:
- 当final关键字修饰成员方法时,这个方法就是最终方法,不能被重写
- 注意:对于类和方法来说,abstract和final关键字不能同时使用
3.修饰局部变量
- 变量是基本类型:final修饰指的是基本类型的数据值不能发生改变
- 变量是引用类型:final修饰指的是引用类型地址值不能发生改变,但是地址里面的内容是可以改变的
4.修饰成员变量
- 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
- 对于final的成员变量,要么直接赋值,要么通过构造方法构造,二选一
- 必须保证类中所有重载的构造方法,都最终会对final的成员变量进行赋值,此时不需要set方法
static关键字
static是静态的意思,可以修饰成员方法,成员变量
static修饰的特点:
- 被修饰的类的所有对象共享(判断是否使用的条件)
- 可以通过对象名调用,也可以用类名调用,推荐用类名
static访问特点:静态成员方法只能访问静态成员
2.多态
2.1 多态概述
同一个对象,在不同时刻表现出来的不同形态
多态的前提和体现
- 有继承/实现关系
- 有方法重写
- 有父类引用指向子类对象 如Animal a=new Cat()
2.2多态中成员访问特点
- 成员变量:编译看左边,执行看左边
- 成员方法:遍历看左边,执行看右边
- 成员方法有重写,成员变量没有
2.3 多态的好处和弊端
- 多态的好处:提高了程序的扩展性
- 多态的弊端:不能使用子类特有的功能(方法)
2.4 多态的转型
向上转型:从子到父,父类引用指向子类对象
向下转型:从父到子,父类引用转为子类对象
例如:
Person p=new student();--父类引用指向子类对象//向上转型,仍然是只能用重写方法
student stu=(student)p;--父类引用转为子类对象//向下转型,既可以用重写的方法,也可以用特有方法
2.5 多态转型的内存图
public class AnimalDemo{
public static void main(String[]args){
//向上转型
Animal a=new Cat();
//向下转型
Cat c=(Cat)a;
c.eat();
c.playGame();
//向上转型
a=new Dog();
a.eat();
//向下转型
Cat cc=(Cat)a;
}
}
public class Animal{
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("猫捉老鼠");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("狗吃肉");
}
}
a.main方法新建Animal对象,由于cat继承Animal,故a的地址值为001
b.a.eat()执行看右边,即cat类的eat方法入栈,输出相应内容
c.a被强制转换为cat类后赋给新的cat c,由于类相同且同继承一个父类,所以可以转换成功,a的地址值将传给c,继续执行到方法后输出相应内容
d. 由于Dog类是对Animal类的继承,故新的对象可以赋给a(向上转型),此时新的对象在堆内存中开辟空间,地址值为002,并将地址传给a
e.执行a.eat()
f.执行向下转型时发现a指向的002地址值,即Dog类的对象, 但是在转化赋值后给的是cat类对象,此时出现类型转换异常
总结:只能有继承关系的两个类或者同类之间可以转化,其他的继承类与本继承类无法转化
3.抽象类
3.1抽象类概述
在Java中,一个没有方法体的方法应该被定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
抽象方法(没有方法体):public abstract void eat();
3.2 抽象类的访问特点
- 抽象类的抽象方法必须使用abstract关键字修饰
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类的实例化:参照多态的方式,通过子类实例化(抽象多态)
- 抽象类的子类要么重写抽象类中所有的抽象方法,要么是抽象类
3.3 抽象类的成员特点
- 成员变量:常量/变量
- 构造方法:有构造方法但无法实例化,构造方法用于子类访问父类数据的初始化
- 成员方法:可以有抽象方法但子类必须完成这些操作,也可以有非抽象方法为了提高代码复用性 (继承)
4.接口
4.1接口概述
接口就是一种公共的规范标准,只要符合规范标准,大家都可以使用
Java中的接口更多的体现在对行为的抽象
4.2 接口的特点
- 接口用关键字interface修饰:public interface 接口名{}
- 类实现接口用implements:public class 类名 implements 接口名{}
- 接口不能实例化:参照多态(接口多态)
- 接口的实现类:要么重写接口中的所有抽象方法,要么是抽象类
4.3 接口的成员特点
- 成员变量:只能是常量,默认修饰符:public static final
- 构造方法:接口没有构造方法
- 成员方法:只能是抽象方法,默认修饰符:public abstract
4.4 类和接口的关系
- 类和类的关系:继承关系,只能单继承,不能多层继承
- 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在一个类同时实现多个接口
- 接口和接口的关系:继承关系,可以单继承,也可以多继承
4.5 抽象类和接口的区别
成员区别:
抽象类:变量、常量、有构造方法、有抽象方法、也有非抽象方法
接口:常量、抽象方法
关系区别:
类和类的关系:继承关系,只能单继承,不能多层继承
类和接口的关系:实现关系,可以单实现,也可以多实现
接口和接口的关系:继承关系,可以单继承,也可以多继承
设计理念区别:
抽象类:对类抽象,包括属性、行为
接口:对行为抽象,主要是行为
总结:抽象类是对事物的抽象,接口是对行为的抽象
4.6 案例
现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都要学习英语,用代码实现
设计思路:
- 分析:从具体到抽象
- 实现:从抽象到具体
- 使用:使用的是具体的类的对象
//说英语的接口
public interface SperkEnglish{
public abstract void speak();
}
//抽象人类
public abstract class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
public abstract void eat();
}
//抽象教练类
public abstract class Coach extends Person{
public Coach(){}
public Coach(String name,int age){
super(name,age);
}
public abstract void teach();
}
//抽象运动员类
public abstract class Player extends Person{
public Player(){}
public Player(String name,int age){
super(name,age);
}
public abstract void learn();
}
//具体乒乓球运动员类
public class PingPongPlayer extends Player{
@Override
public void learn(){
System.out.println("乒乓球运动员学下旋球");
}
@Override
public void eat(){
System.out.println("乒乓球运动员吃蔬菜");
}
}
//具体的乒乓球教练类
public class PingPongCoach extends Coach{
@Override
public void teach(){
System.out.println("乒乓球教练教下旋球");
}
@Override
public void learn(){
System.out.println("乒乓球运动员学下旋球");
}
}
//具体的篮球运动员类{
@Override
public void learn(){
System.out.println("篮球运动员学精准投篮");
}
@Override
public void eat(){
System.out.println("篮球运动员吃羊肉");
}
}
//具体的篮球教练类{
@Override
public void teach(){
System.out.println("篮球教练教精准投篮");
}
@Override
public void eat(){
System.out.println("篮球教练吃水果");
}
}
public class PersonDemo{
public static void main(String[]args){
//创建对象
PingPongPlayer ppp=new PingPongPlayer();
ppp.setName("Nick");
ppp.setAge(20);
System.out.println(ppp.getName()+","+ppp.getAge());
ppp.eat();
ppp.study();
ppp.speak();
System.out.println("-------------");
BasketballPlayer bbb=new BasketballPlayer();
bbb.setName("Jack");
bbb.setAge(21);
System.out.println(bbb.getName()+","+bbb.getAge());
bbb.eat();
bbb.study();
bbb.speak();
}
}
5*.形参和返回值
5.1 类名作为形参和返回值
- 方法的形参是类名,其实需要的是该类的对象
- 方法的返回值是类名,其实返回的是该类的对象
5.2 抽象类名作为形参和返回值
- 方法的形参是抽象类名,其实需要的是该抽象类子类的对象
- 方法的返回值是抽象类名,其实返回的是该抽象类子类的对象
5.3 接口名作为形参和返回值
- 方法的形参是接口名,其实需要的是该接口的实现类的对象
- 方法的返回值是接口名,其实返回的是该接口的实现类的对象
6.内部类
6.1 内部类概述
内部类:在一个类中定义一个类
内部类的定义格式:
public class 类名{
修饰符 class 类名{
}
}
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
public class Outer{
private int num=10;
public class Inner{
public void show(){
System.out.println(num);//10
}
}
public void method(){
//show();//不可以
Inner i=new Inner();
i.show();//即可
}
}
6.2 成员内部类
按照内部类在类中定义的位置不同,可以分为以下两种形式
- 再类的成员位置:成员内部类
- 在类的局部位置:局部内部类
成员内部类修饰符为public,外界的公共创建方法
- 格式:外部类名.内部类名 对象名=外部类对象.内部类对象;
- 比如:Outer.Inner oi=new Outer().Inner();
成员内部类修饰符为private,外界的私有创建方法
在外部类中定义一个方法(定义内部类的对象,并调用内部类方法),方法中创建了内部类对象,在外界类中直接用外部类的对象调用该方法即可访问内部类的方法
public class Outer{
private int num=10;
private class Inner{
public void show(){
System.out.println(num);
}
}
public void method(){
Inner i=new Inner();
i.show();
}
}
public class T2{
public static void main(String[]args){
Outer o=new Outer();
o.method();
}
}
6.3 局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
public class Outer{
private int num=10;
public void method(){
int num2=20;
class Inner{
public void show(){
System.out.println(num);
}
}
Inner i=new Inner();
i.show();
}
}
public class T2{
public static void main(String[]args){
Outer o=new Outer();
o.method();
}
}
6.4 匿名内部类
前提:如果接口的实现类,或者是父类的子类,只需要用唯一的一次。那么这种情况下就可以用匿名内部类。
格式:
new 类名或者接口名(){
重写方法;
}
本质:是一个继承了该类或者实现了该接口的子类匿名对象
public interface MyInterface{
public abstract void method();
}
public class MyInterfaceImpl implements MyInterface{
@Override
public void method(){
System.out.println("实现类重写了方法");
}
}
public class DemoMain{
public static void main(String[]args){
MyInterface obj = new InterfaceImply();
obj.method();
}//实现类实现
//使用匿名类
MyInterface obj =new MyInterface(){
@Override
public void method(){
System.out.println("匿名类实现了方法");
}
};
}
注意:
1.匿名内部类,在创建对象的时候只能使用唯一一次,如果希望多次创建对象,而且类的内容一样的话最好还是创建实现类。
2.匿名对象,在调用方法的时候,只能使用一次,如果希望同一个对象调用多个方法,必须得给匿名类对象起一个名字。
3.匿名内部类是省略了父类的子类,但是匿名对象省略的是名称