一、继承
1.1 继承的概述
1.多个类中存在相同的属性和行为是,将这些相同的内容抽取到单独的一个类中,这样多个类中就不用去定义这些相同的属性和行为,只要去继承抽取出来的那个类即可。
2.继承的格式:使用关键字extends可以实现继承的关系
class 子类 extends 父类{ }
父类也可以称为基类或者超类。
3.代码实现继承的关系
//定义一个父类
public class FU {
String name;
int age;
public void eat(){
System.out.println(name+"的年龄为"+age);
}
}
//子类实现继承
class Zi extends FU{
}
//测试类
public class Test {
public static void main(String[] args) {
//定义子类的一个对象zi,通过对象调用继承父类的成员变量和成员方法,可以看到正常运行
Zi zi = new Zi();
zi.name="张三";
zi.age=23;
zi.eat();//张三的年龄为23
}
}
4.继承的利与弊:
好处:a.提高了代码的复用性
b.提高了代码的维护性
c.让类与类之间的发生了关系,是多态的前提
弊端:a.增加了类的耦合性
在开发的过程中:讲究的就是高内聚,低耦合。
内聚:就是自己完成某件事的能力
耦合:就是类与类之间的关系
5.继承的特点:
继承只支持单继承,不支持多继承。但是Java支持多层继承,也就是继承体系,继承父类的子类同样也可以拥有子类。
6.注意事项:
- 子类只能继承父类所有的非私有的成员方法和成员变量
- 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法
- 不能为了部分功能而且继承
7.继承是成员变量的关系
子类的成员变量和父类的成员变量不一样,通过子类中去调取不同的变量名即可。
子类的成员变量和父类的成员变量一样,代码查看运行结果:
//父类与子类的成员变量名相同,在子类方法中进行调用。
class Fu {
//父类的成员变量
int num = 22;
}
class Zi extends Fu {
//子类的成员变量
int num = 23;
//子类的成员方法
public void show() {
System.out.println("父类: " + num);//23
System.out.println("子类:" + num);//23
}
}
class Test{
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show()方法
z.show();
}
}
可以看出运行结果两个都是子类的成员变量,并没有调取父类的成员方法。这是因为在子类中的变量的访问的一个查找顺序(就近原则)
- 在子类的方法局部内进行查找,有就使用
- 在子类的成员范围内进行查找,有就使用
- 在父类的成员范围内进行查找,有就使用
- 如果都没有找到,则报错
1.2 super关键字
1.super关键字可以在子类的局部范围内访问父类的成员变量
2.this和super的区别
this 代表的是本类对象的引用
super 代表的是父类存储空间的标识(父类的引用,操作父类的成员)
3.this和super的使用
(1)调用成员变量
this.成员变量 调用本类的成员变量
super.成员变量 调用父类的成员变量
(2)调用构造方法
this() 调用本类的构造方法
super() 调用父类的构造方法
(3)调用成员方法
this.成员方法 调用本类的成员方法
super.成员方法 调用父类的成员方法
对上述代码使用super关键字:
//父类与子类的成员变量名相同,在子类方法中使用super关键字进行调用。
class Fu {
//父类的成员变量
int num = 22;
}
class Zi extends Fu {
//子类的成员变量
int num = 23;
//子类的成员方法
public void show() {
System.out.println("父类: " + super.num);//22
System.out.println("子类:" + num);//23
}
}
class Test{
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show()方法
z.show();
}
}
可以看出,使用super关键字之后的num访问的是父类的成员变量
1.3 继承中的构造方法
1.子类中所有的构造方法默认都会访问父类中空参数的构造方法
2.子类继承父类中的数据,可能还会使用父类中的数据,这时候子类在进行初始化之前,一定要先完成父类数据的初始化
3.每一个子类的构造方法的第一条语句默认都是super()
4.构造方法中的注意事项:
当父类没有无参构造方法时,子类怎么办?(父类中定义了有参构造,系统将不会默认给出无参构造)
- 在父类中添加一个无参的构造方法
- 子类通过super去显示调用父类其他的带参的构造方法
- 子类通过this去调用本类的其他构造方法(本类其他构造也必须首先访问父类构造)
super(…)和this(…)必须出现在第一条语句上
public class Test02 {
public static void main(String[] args) {
Zi1 zi1 = new Zi1();//子类的无参构造将name传递给子类的有参构造,将name返回父类的有参构造,输出 name
Zi1 zi2 = new Zi1("张三");//将张三传递给子类的有参构造,将张三返回父类的有参构造,输出 张三
System.out.println(zi1);//打印地址值
System.out.println(zi2);//打印地址值
}
}
class Fu1{
String name;
public Fu1(String name){
System.out.println(name);
}
}
class Zi1 extends Fu1{
String name1;
//父类没有无参构造,默认报错
// public Zi1(){
//
// }
//父类只存在有参构造,所有子类的有参构造必须第一行使用super(参数)
public Zi1(String name){
super(name);
}
//无参构造,必须第一行使用this调用本类的其他构造方法
public Zi1(){
this("name");
}
}
1.4 继承中的成员方法关系
1.当子类的方法名和父类的方法名不一样的时候,使用对象.方法名调用即可
2.当子类的方法名与父类的方法名一样的时候:通过子类调用方法
(1).先查看子类中有没有此方法,有就使用
(2).再看父类有没有此方法,有就使用
(3).如果都没有就报错
public class Test03 {
public static void main(String[] args) {
Dog dog = new Dog();
//父类与子类的方法名不一样,直接调用
dog.sleep1();//狗在睡觉
dog.sleep();//动物睡觉
//父类与子类的方法名一样,子类中有这个方法,则使用的是子类的方法
dog.eat();//狗在吃饭
}
}
class Animals{
String name;
public void eat(){
System.out.println("动物吃饭");
}
public void sleep(){
System.out.println("动物睡觉");
}
}
class Dog extends Animals{
public void eat() {
System.out.println("狗在吃饭");
}
public void sleep1() {
System.out.println("狗在睡觉");
}
}
1.5 方法重写
1.注意事项:
- 父类中私有的方法不能被重写(父类的私有方法不会被子类所继承)
- 子类重写父类方法时,访问权限不能更低(最后一致)
- 静态方法属于类,不参与重写。(静态方法优先于对象的创建)
- 子类重写父类方法的时候,最好声明一模一样
public class Test01 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();//show方法
Zi.test();//通过类名调用静态方法
Fu.test();//通过类名调用静态方法
}
}
class Fu{
public void show(){
System.out.println("show方法");
}
public static void test(){
System.out.println("父类的静态方法");
}
}
class Zi extends Fu{
//重写父类方法的快捷键 ctrl+o
@Override
public void show() {
super.show();
}
public static void test(){
System.out.println("子类的静态方法");
}
}
1.6 final关键字
1.由于继承中会有方法重写,而有时候不让子类去重写父类的方法,这种情况下要使用final关键字
2.final关键字:可以修饰类,变量和成员方法
3.final的修饰特点:(1)修饰类: 被修饰的类不能
(2)修饰成员变量: 被修饰的变量不能被重新赋值,因为这个量是一个常量
(3)修饰方法: 被修饰的方法不能够被重写
4.final修饰成员变量是,成员变量名使用大写
5.final修饰局部变量的时候:(1)基本类型: 值不能改变
(2)引用类型: 地址值不能改变
public class Test {
//final修饰的值不能被该改变
private final int MENU_01=23;
public static void main(String[] args) {
final int NUM=200;
System.out.println(NUM);//200
new C().test();//子类Test方法
new C().show();//show方法
//final修饰的引用类型地址值不能改变
final B b=new B();
System.out.println(b);//B@1540e19d
}
}
class A{
//final修饰的方法不能够重写
public final void show(){
System.out.println("show方法");
}
public void test(){
System.out.println("父类test方法");
}
}
final class B{
}
class C extends A{
//重写test方法
@Override
public void test() {
System.out.println("子类Test方法");
}
}
二、多态
2.1 多态的概述
1.多态:某一种事物,在不同时刻表现出来的不同状态
Cat cat=new Cat()
Animal cat=new Cat()
上述两行表示一个对象可以表示两种类型:猫可以是猫,也可以是动物
2.多态的前提:
- 要有继承关系
-要有方法的重写
要有父类引用指向子类对象
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();//猫吃饭
//父类对象指向子类引用
Animal cat1=new Cat();
cat1.eat();//猫吃饭
}
}
class Animal{
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃饭");
}
}
2.2 多态中的成员访问特点
1.成员访问特点:
a.成员变量:编译看左,运行看左
b.构造犯法:创建子类对象的时候,会访问父类的构造方法,对父类的数据进行初始化
c.成员方法:编译看左,运行看右
d.静态方法:编译看左,运行看左(静态方法与类相关,访问还是左边的)
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println(zi.name);//李四
zi.show();//子类的show方法
//采用多态的形式,去访问成员变量 访问的是父类 编译看左边,运行也看左边
Fu zi1=new Zi();
System.out.println(zi1.name);//张三
//采用多态的形式,去调用成员方法,如果有重写。编译看左边,运行看右边
zi1.show();//子类的show方法
Fu.test();//父类的静态方法
Zi.test();//子类的静态方法
}
}
class Fu{
String name="张三";
public void show(){
System.out.println("父类的show方法");
}
public static void test(){
System.out.println("父类的静态方法");
}
}
class Zi extends Fu{
String name="李四";
@Override
public void show() {
System.out.println("子类的show方法");
}
public static void test(){
System.out.println("子类的静态方法");
}
}
2.3 多态的好处与弊端
1.提高了代码的维护性(继承保证)
2.提高了代码的扩展性(多态保证)
3.弊端:不能使用子类特有的功能
2.4 多态中的向上转型和向下转型
把父类的引用强制转换为子类的引用
public class Test {
public static void main(String[] args) {
//向上转型
Fu fu=new Zi();
fu.show();//子类的show方法
//fu.test(); 由于父类中不存在test方法,编译看左,所以不能够直接调用test方法
//这也是一种向下转型的方式,调用子类特有的方法
((Zi) fu).test();
//向下转型
Zi zi= (Zi) fu;
zi.test();//子类特有的test方法
}
}
class Fu{
public void show(){
System.out.println("父类的show方法");
}
}
class Zi extends Fu{
@Override
public void show() {
System.out.println("子类的show方法");
}
public void test(){
System.out.println("子类特有的test方法");
}
}