【JavaSE】面向对象

目录

一、包

二、继承

2.1 继承的意义

2.2 如何实现继承

question1:在Java中,一个子类能同时继承多个父类吗?

question2:子类能全部继承父类所有的public的字段和方法吗?

question3:子类能继承父类的private修饰的字段和方法吗?

question4:设置为private,子类不能访问,设置成public,又违背了封装的初衷,有什么好的解决办法吗?

question5:子类能继承父类的构造方法吗?

question6:this和super的区别?

question7:如果子类和父类的字段重名,子类对象调用的是谁的字段?

question8:如果子类和父类的字段重名,子类对象想调用父类的字段,怎么办?

question9:如果子类和父类的方法重名,怎么办?

question10:如果不想被继承,应该怎么办?

question11:继承还有哪些注意事项呢?

三、组合

四、多态

4.1 向上转型

4.2 动态绑定

4.3 向下转型

4.4 方法重写

question1:重载和重写的区别

question2:重写的注意事项

question3:在构造方法中调用重写的方法

4.5 理解多态

4.6 多态的好处

五、抽象类

5.1 什么是抽象类:

5.2 抽象类有啥:

5.3 抽象类的特点:

六、接口

6.1 什么是接口?

6.2 接口的特点

6.3 类和接口的关系是啥?

6.4 接口和接口的关系?

6.5 抽象类和接口的区别


一、包

包:是组织类的一种形式。使用包的主要目的是保证类的唯一性。

包名:全小写,最好是公司域名的逆置

包访问权限,就是只能在当前包中使用

包实际上,可以看作一个文件夹。

jia包里面包含的都是字节码文件。

java.long这个包,不需要手动导入

java.util:工具程序包

二、继承

2.1 继承的意义

观察下面的代码,我们可以观察出里面有大量的冗余代码。通过分析,Animal、Cat和Bird这三个类中都有相同的name属性、age属性、eat()方法,同时,从逻辑上讲,Cat和Bird都是一种animal,因此,我们可以让Cat和Bird分别继承Animal类。

class Animal{
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
}
class Cat{
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
    public void barkMiao(){
        System.out.println("喵喵喵");
    }
}
class Bird{
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
    public void fly(){
        System.out.println(this.name +"飞");
    }
}
public class Main2 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Bird bird = new Bird();
    }
}

引入继承后,修改后的代码如下:

class Animal{
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
}
class Cat extends Animal{
    public void barkMiao(){
        System.out.println("喵喵喵");
    }
}
class Bird extends Animal{
    public void fly(){
        System.out.println(this.name +"飞");
    }
}
public class Main2 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "小猫咪";
        cat.eat();  
        Bird bird = new Bird();
        bird.name = "小麻雀";
        bird.eat();
    }
}

我们可以看到,当子类继承了父类之后,子类就拥有了父类的方法和属性。因此,继承的意义是方法的重复使用。

2.2 如何实现继承

如何实现继承:

第一步:先抽取共性,放在父类中

第二步:使用extends关键字继承

question1:在Java中,一个子类能同时继承多个父类吗?

在Java中,只能是单继承,不能是多继承。

在C++、python等语言支撑多继承。

question2:子类能全部继承父类所有的public的字段和方法吗?

会,子类会继承父类的所有的public的字段和方法

question3:子类能继承父类的private修饰的字段和方法吗?

子类可以继承,但是不能访问。

question4:设置为private,子类不能访问,设置成public,又违背了封装的初衷,有什么好的解决办法吗?

引入protected关键字。对于类的子类(同一个包中或者不同包中)和同一个包中的其他类,protected修饰的字段是可以访问的。

范围privatedefault(是包访问权限,不是关键字,什么都不用写)protectedpublic
同一包中的同一类
同一包中的不同类
不同包中的子类(不同包中可能会有继承关系)
不同包中的非子类

question5:子类能继承父类的构造方法吗?

因为构造方法不能被继承,只能被显示的引用。子类在继承父类时,需要先帮助父类构造,使用super(),这个的意思就是显示的调用父类的构造方法。

class Animal{
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
    public Animal(String name,int age){
        this.name = name;
        this.age = age;
    }
    public Animal(String name){
        this.name = name;
    }

}
class Cat extends Animal{
    public void barkMiao(){
        System.out.println("喵喵喵");
    }
// 如果不写构造方法,编译器会默认提供一个不带参数的构造方法
// 如果父类中写了构造方法,在子类中就得使用super()显示的告诉编译器用的是哪个构造方法
    public Cat(String name){
        super(name); 
    }
    public Cat(String name,int age){
        super(name,age); 
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Main2 {
    public static void main(String[] args) {
        Cat cat1 = new Cat("小黄");
        System.out.println(cat1.toString());
        Cat cat2 = new Cat("小黑",3);
        System.out.println(cat2.toString());
    }
}

question6:this和super的区别?

this是指当前对象的引用。super是指当前对象父类的引用。

this和super都可以调用对象的构造函数、参数和方法。在这两个都调用构造函数时,都必须放在第一行。因为第一行只有一个,所以super()和this()不能共存

super和this都依赖对象,static不依赖对象,所以这两个都不能在static方法里面使用。

question7:如果子类和父类的字段重名,子类对象调用的是谁的字段?

如果子类和父类的字段重名,调用时采用就近原则。

class Animal{
    public String name = "父类的小猫";
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
}
class Cat extends Animal{
    public String name = "子类的小猫";
    public void barkMiao(){
        System.out.println("喵喵喵");
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Main2 {
    public static void main(String[] args) {
        Cat cat1 = new Cat();
        System.out.println(cat1.toString());
    }
}

运行结果:

question8:如果子类和父类的字段重名,子类对象想调用父类的字段,怎么办?

在子类方法中,如果想调用父类的字段,使用super。

question9:如果子类和父类的方法重名,怎么办?

如果子类的方法和父类方法有以下特征:

方法名相同、返回值相同和参数列表相同,则子类方法重写了父类方法。

question10:如果不想被继承,应该怎么办?

当这个类不想被继承,加上final修饰,称为密封类。

final还可以修饰方法,称为密封方法。还可以修饰变量或者字段,称为常量。

question11:继承还有哪些注意事项呢?

在继承时,最多不要超过三层。

三、组合

面向对象还有一个特点,组合。将一个类的实例作为另一个类的字段。也是能够起到代码重用的效果。

class Student{
    
}
class Teacher{
    
}
class School{
    public Student[] students;
    public Teacher[] teachers;
}

四、多态

4.1 向上转型

向上转型是指:父类引用 指向 子类对象

发生的三种时机:

直接赋值: Animal animal = new Cat();

方法的传参:方法的参数类型是父类引用,但是调用时传的是子类对象

返回值:方法的返回值是父类引用,但是返回的是子类对象。

class Animal{
    public String name = "父类的小猫";
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭");
    }
}
class Cat extends Animal{
    public String name = "子类的小猫";
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Main2 {
    public static void main(String[] args) {
        Animal animal = new Cat(); // 直接赋值
    }
    // 方法的返回值
    public static Animal findCat(){
        Cat cat = new Cat();
        return cat;
    }
    // 方法的传参
    public static void main2(String[] args) {
        Cat cat = new Cat();
        feed(cat);
    }
    public static void feed(Animal animal){
    }
}

4.2 动态绑定

第一步,发生向上转型,即父类引用指向子类对象

第二步:通过父类引用,调用子类和父类同名的override方法。

结论:编译时调用的父类的方法,实际上调用的是子类的方法。

class Animal{
    public String name = "小猫";
    public int age;
    public void eat(){
        System.out.println(this.name+ "吃饭饭,是父类的方法哦");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println(this.name+ "吃饭饭,是子类的方法哦");
    }
}
public class Main2 {
    public static void main(String[] args) {
        Animal animal = new Cat(); // 向下转型,父类引用 指向子类对象
        animal.eat();  // 通过父类引用 调用子类和父类同名的override方法
    }
}

运行结果:

4.3 向下转型

上下转型的前提是,发生了向上转型,同时,向下转型在使用时必须进行强制类型转换

向下转型极度不安全,不建议使用。

instanceof可以判定一个引用是否是某个类的实例,如果是,则返回true,此时再进行向下转型时就比较安全了。

4.4 方法重写

子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override)

question1:重载和重写的区别

重写:方法名、参数列表都一样,返回值最好都一样。

重载:方法名相同,参数列表不同,返回值不做要求

question2:重写的注意事项

重写的方法不能是密封方法,即被final修饰。

子类的访问修饰限定符的权限一定要大于等于父类的权限:

private < final < proteced < public

父类不能被private修饰。

重写的方法也不能被static修饰。

question3:在构造方法中调用重写的方法

class ClassA{
    public ClassA(){
        func();
    }
    public void func(){
        System.out.println("这是父类的func()");
    }
}
class classB extends ClassA{
    public int a = 1;
    @Override
    public void func() {
        System.out.println("这是子类的func()"+a);
    }
}
public class TestDemo4{
    public static void main(String[] args) {
        //子类不能继承父类的构造方法,只能显示的引用父类的构造方法,即子类在构造前,需要先帮父类构造
        // 先帮父类构造,调用父类的构造函数,在父类的构造函数里面有个func()函数
        // 此处发生了动态绑定,调用的子类的func()函数,由于public int a = 1;这行代码并没有被执行
        // 所以输出是 这是子类的func()0  
        classB classB = new classB(); //运行结果:这是子类的func()0
    }
}

尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题。

4.5 理解多态

了解向上转型、动态绑定和方法重写后,就可以利用多态的形式来设计程序了。可以重写一些只关注父类的代码,同时可以兼容各类子类的情况。

class Shape{
    public void draw(){
        // 啥都不用实现,这个方法就是让子类重写的
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("画⚪");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("画正方形");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("画花");
    }
}
//--------上面的代码是 类的实现者编写的 ,下面的代码是类的调用者编写的
public class TestDemo4 {
    // 这个方法的参数类型为Shape,在这个方法内部不知道也不关注当前的shape指向哪个子类型的实例
    // 所以shape这个引用调用draw()方法可能会有多种不同的表现,这种行为就称为多态
    public static void drawShape(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        Shape shape1 = new Cycle();
        Shape shape2 = new Flower();
        Shape shape3 = new Rect();
        drawShape(shape1);  //画⚪
        drawShape(shape2); //画花
        drawShape(shape3); //画正方形 
    }
}

4.6 多态的好处

①类调用者对类的使用成本进一步降低。

         封装让类的调用者不需要知道类的实现细节。

        多态能让类的调用者连这个类的类型都不必知道,只需要知道这个对象具有某个方法即可。

②能够降低代码的圈复杂度,避免使用大量的if-else

圈复杂度:是一种描述一段代码复杂程度的方式,一段代码如果平铺直叙,就比较容易理解。而如果有很多的条件分支或者循环语句,就认为理解起来更复杂。

可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数,该个数就称为圈复杂度。如果一个方法的圈复杂度太高,就需要考虑重构。

不同公司对于代码的圈复杂度的规范不一样,一般不会超过10.

举一个例子:

// 不适用多态 假设打印多个形状
    public static void drawShapes1(Shape shape){
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Flower flower = new Flower();
        String[] shapes = {"rect","cycle","flower","cycle"};
        for(String str:shapes){
            if(str.equals("rect")){
                rect.draw();
            }else if(str.equals("cycle")){
                cycle.draw();
            }else if(str.equals("flower")){
                flower.draw();
            }
        }
    }
    // 使用多态
    public static void drawShapes2(Shape shape){
        // 会根据传入的对象,调用合适的方法
        Shape[] shapes = {new Cycle(),new Rect(),new Flower(),new Rect()};
        for(Shape shape1 :shapes){
            shape1.draw();
        }
    }

③可扩展能力更强

假设现在想要新增一种新的情况,使用多态的方式修改代码的成本也很低。

class Star extends Shape{
    @Override
    public void draw() {
        System.out.println("星星");
    }
}

对于类的调用者来说,只要创建一个新类的实例就行,改动成本很低。如果不用多态,就需要修改drawShape1中的if-else。

五、抽象类

5.1 什么是抽象类:

如果类中的方法被子类继承后会重写,那这个方法就不用具体的实现,因为不管你实现啥,子类都会重写,此时,就可以用abstract修饰,就可以不用具体的实现了。

由abstract修饰的方法是抽象方法,包含抽象方法的类是抽象类,抽象类也由abstract修饰。

5.2 抽象类有啥:

抽象类里面由抽象方法、普通的成员变量、普通的成员方法

5.3 抽象类的特点:

抽象类不能被实例化

不能实例化要他干啥:

就是为了被继承。(抽象类也是类,也只能被一个类继承)

继承了抽象类然后呢?

首先,抽象类中的抽象方法不能被private修饰,子类需要重写抽象类的所有抽象方法。

抽象类继承抽象类:不需要重写所有的抽象方法

普通类继承抽象类:需要重写所有的抽象方法

六、接口

6.1 什么是接口?

由Interface修饰,接口中不能有普通方法,必须全是抽象方法。

抽象方法默认为:public abstract  ,写的时候可以省略 public abstract

成员变量默认为:public static final 修饰的  ,写的时候可以省略 public static final

interface Ishape{
    public static final int a = 0;
    void draw();
}
class Cycle1 implements Ishape{
    @Override
    public void draw() {
        System.out.println("⚪");
    }
}
public class TestDemp6 {
    public static void main(String[] args) {
        Ishape ishape = new Cycle1();
        ishape.draw();   
    }
}

6.2 接口的特点

不能被实例化

不能实例化,那他的作用是啥?

为了被多继承

interface Ishape{
    public static final int a = 0;
    void draw();
}
interface Color{
    void paint();
}
class Cycle1 implements Ishape, Color {
    @Override
    public void draw() {
        System.out.println("⚪");
    }
    @Override
    public void paint() {
        System.out.println("上色");
    }
}
public class TestDemp6 {
    public static void main(String[] args) {
        Cycle1 cycle1 = new Cycle1();
        cycle1.draw();
        cycle1.paint();
    }
}

6.3 类和接口的关系是啥?

实现 implements ,同时类需要实现接口里的所有抽象方法,同时,一个类可以实现多个接口,接口之前用逗号隔开.

6.4 接口和接口的关系?

是extends,扩展。该接口拓展了其他接口的功能。

6.5 抽象类和接口的区别

区别抽象类接口
结构组成普通成员变量、普通成员方法、抽象方法抽象方法+全局常量
权限各种权限

都是public(抽象方法:public abstract)

全局常量:public static final

子类使用

不同类继承抽象类:继承 extends 需要实现所有的抽象方法

抽象类继承抽象类:不需要实现所有的抽象方法

接口和接口之间:extends 不需要实现所有的抽象方法

抽象类和接口:实现,implements  需要实现每个接口的所有抽象方法

限制类只能是单继承,抽象类也不例外一个子类可以实现多个接口
相同点都不能被实例化
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘减减

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值