面向对象中的注意点




//父类Animal
class Animal {  
/*8、执行初始化*/  
    private int i = 9;  
    protected int j;  
 
/*7、调用构造方法,创建默认属性和方法,完成后发现自己没有父类*/  
    public Animal() {  
/*9、执行构造方法剩下的内容,结束后回到子类构造函数中*/  
        System.out.println("i = " + i + ", j = " + j);  
        j = 39;  
     }  
 
/*2、初始化根基类的静态对象和静态方法*/  
    private static int x1 = print("static Animal.x1 initialized");  
    static int print(String s) {  
        System.out.println(s);  
        return 47;  
    }  
}  
 
//子类 Dog
public class Dog extends Animal {  
/*10、初始化默认的属性和方法*/ 
    private int k = print("Dog.k initialized");  
 
/*6、开始创建对象,即分配存储空间->创建默认的属性和方法。 
     * 遇到隐式或者显式写出的super()跳转到父类Animal的构造函数。
     * super()要写在构造函数第一行 */  
    public Dog() { 
/*11、初始化结束执行剩下的语句*/
        System.out.println("k = " + k);  
        System.out.println("j = " + j);  
    }  
 
/*3、初始化子类的静态对象静态方法,当然mian函数也是静态方法*/  
    private static int x2 = print("static Dog.x2 initialized");
 
/*1、要执行静态main,首先要加载Dog.class文件,加载过程中发现有父类Animal, 
    *所以也要加载Animal.class文件,直至找到根基类,这里就是Animal*/       
    public static void main(String[] args) {  
 
/*4、前面步骤完成后执行main方法,输出语句*/ 
        System.out.println("Dog constructor"); 
/*5、遇到new Dog(),调用Dog对象的构造函数*/  
        Dog dog = new Dog();   
/*12、运行main函数余下的部分程序*/            
        System.out.println("Main Left"); 
    }  
}

继承

两种必须要使用super调用父类的构造方法的情况:

  1. 父类中没有空参数构造方法,此时子类的构造方法里必须使用super调用父类的构造方法。
  2. 父类的构造方法用到了私有变量,子类使用this无法访问到,需要使用super访问。
class 父类类名 {
    ...
}
class 子类类名 extends 父类类名 {
    ...
}

super的使用

class Animal {
    private String name;
    private int 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;
    }

    Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Dog extends Animal {
    Dog(String name, int age) {
        super(name, age);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("jerry", 2);
    }
}

抽象类

abstract class 类名字 { 
    public abstract void 方法名(); 
}

1.一个抽象类里可以有抽象方法,也可以没有抽象方法

2.如果一个类有了抽象方法则必须声明抽象类

abstract class Test{
    public abstract void demo();  // 只要有了抽象方法,那么这个类必须要抽象
}

3.抽象类不能直接创建对象,如果一定要创建,需要实现(重写)抽象方法。

4.通常情况下,我们会创建一个继承抽象类的子类,在子类里实现所有的抽象方法,然后去创建子类对象。

5.如果子类没有实现父类所有的抽象方法,如只实现了其中一个,那么这个子类也需要被定义成为抽象的。

abstract class Animal {
    public abstract void shout();
    public abstract void eat();
}
abstract class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("小狗正在汪汪汪");
    }
}

final

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,不能被重新赋值。如果局部变量时基本数据类型,被final修饰后,只能赋值一次,再次赋值会报错。如果局部变量是引用数据类型,在被final修饰后,只能指向一次某个对象,不允许再修改指向。但是不影响对象内部的成员变量值的修改。

接口

使用interface修饰,格式如下:

interface Flyable{
    //静态常量
    //(1)公共的静态的常量:其中public static final可以省略。
    //(2)公共的抽象的方法:其中public abstract可以省略。
    long MAX_SPEED = 7900000;//这里单位是毫米/秒,7.9千米/秒,超过这个速度,就变成卫星
    //抽象方法
    void fly();   
    //默认方法
    public default void start(){
        System.out.println("开始");
    }
    public default void stop(){
        System.out.println("结束");
    }
    //静态方法
    public static void broken(){
        System.out.println("飞行中遇到物体就坏了");
    }
}

在接口中不能定义构造方法

接口不能直接创建实例对象,如果要创建,需要实现抽象方法。通常不会直接使用接口创建实例对象,会定义一个子类,让它实现接口,再创建子类的实例对象。

使用关键字 implements 通过类实现接口

【修饰符】 class 实现类  implements 接口{
    // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
      // 重写接口中默认方法【可选】
}
【修饰符】 class 实现类 extends 父类 implements 接口{
    // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
      // 重写接口中默认方法【可选】
}

非抽象子类实现接口

  1. 如果一个类实现了接口,必须重写接口中所有抽象方法。如果这个类没有重写所有的抽象方法,那么这个类必须要声明为抽象的!

  2. 接口不能实现接口,可以继承接口,而且一个接口可以继承多个接口。

  3. 继承了接口的默认方法,即可以直接调用,也可以重写。

    重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了

  4. 不能重写静态方法

class Bird implements Flyable{

    //重写/实现接口的抽象方法,【必选】
    public void fly() {
        System.out.println("展翅高飞");
    }

    //重写接口的默认方法,【可选】
    //重写默认方法时,default单词去掉
    public void start(){
        System.out.println("先扇两下翅膀,一蹬腿,开始飞");
    }
}

如何调用对应的方法

  • 对于接口的抽象方法、默认方法,通过实现类对象就可以调用
  • 但是对于静态方法,必须使用接口名才能调用。
public class TestInteface {
    public static void main(String[] args) {
        //创建实现类对象
        Bird b = new Bird();

        //通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法
        b.start();
        b.fly();
        b.stop();

        //通过接口名调用接口的静态方法
        Flyable.broken();
    }
}

单继承多实现

一个类可以继承一个父类,但这个类可以实现多个接口。

继承和多实现的方法冲突问题:

package com.atguigu.inter;

/*
继承和多实现的方法冲突的问题

Son类继承自Father类;实现AInterface,BInterface,CInterface;CInterface继承自AInterface和BInterface

如果在AInterface和BInterface里都定义了 test7 方法,Son类会报错。
因为 Son类实现了 AInterface和BInterface,但是这两个接口都有同名的方法,此时Son创建出来的实例对象不能明确到底调用的是哪个父接口的方法
此时需要在 Son类里重写接口里的 test7方法
Son类在重写 test7 方法时,可以使用 父接口名.super.test7() 指定到底调用哪个父接口的 test7() 方法
 */
public class SonDemo {
    public static void main(String[] args) {
        Son s = new Son();

        // 子类有这个方法,其他父类以及接口都没有的方法
        s.test1();  // 调用的是子类的test1

        // 子类和父类都有这个方法,父接口没有这个方法
        // 子类重写父类的方法,调用子类的方法
        s.test2();  // 调用的是子类的test2

        // 子类没有这个方法,父类有这个方法,子类直接调用父类的方法
        s.test3(); // 调用的是父类的test3

        // 子类父类以及父接口都有test4方法,调用子类
        s.test4(); // 调用子类的test4方法


        // 子类没有,父类和父接口都有
        s.test5(); // 调用父类的test5方法

        // 子类和父类没有,父接口有
        s.test6();  // 调用AInterface里的test6方法

        // 本类 > 父类 > 父接口
        s.test7();
    }
}

多态

class Test {
    public static void main(String[] args) {
        // 父类的引用 stu 指向了一个 new Student() 子类对象————>多态
        Person stu = new Student();
        stu.sleep();  // 子类重写了父类的方法,执行的是子类自己的方法
    }
}

class Person {
    void sleep() {
        System.out.println("人正在睡觉");
    }
}

class Student extends Person {
    @Override
    void sleep() {
        System.out.println("学生正在教室里睡觉");
    }
}

编译看左边,运行看右边

直接通过对象名称访问成员变量,等号左边是谁,优先用谁

对象初始化顺序:

1.先将成员变量赋值为默认值。
2.执行构造方法里的super,初始化父类
2.1给父类里的成员变量设置默认值
2.2调用构造方法里的super,初始化父类对象
2.3执行构造代码块以及给成员变量赋值
2.4执行构造方法里的其他代码
3.执行构造代码块里的内容以及给成员变量赋值(按照代码块和成员变量书写的先后顺序来执行)
4.执行构造方法里的其他内容。

类的初始化顺序:

1.静态成员变量设置默认值。

2.父类初始化。

3.执行静态代码块以及给静态变量赋值,按照书写的顺序执行,谁先写先执行谁

4.同步代码块:用在多线程里保证线程的安全。

引用数据类型转换

多态本身就是向上转型

向下转型:

子类类型 变量名 = (子类类型) 父类变量名;:Cat c =(Cat) a; 

为什么要类型转换

调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

public class Test {
    public static void main(String[] args) {
        Animal dog = new Dog();
//        dog.watchDoor();  编译报错,调用方法时,编译时看左边的类型,是Animal,Animal类里没有 watchDoor方法
        ((Dog) dog).watchDoor();
    }
}
class Animal {
    String type = "动物";
}
class Dog extends Animal {
    public void watchDoor() {
        System.out.println("狗正在看门");
    }
}

向下转型时 容易出现ClassCastException异常。Java提供关键字instanceof,用来判断

格式

变量名 instanceof 数据类型 
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false
public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        if (a instanceof Cat){
            Cat c = (Cat)a;       
            c.catchMouse();        // 调用的是 Cat 的 catchMouse
        } else if (a instanceof Dog){
            Dog d = (Dog)a;       
            d.watchHouse();       // 调用的是 Dog 的 watchHouse
        }
    }  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值