看完这篇博客,面向对象直接拿下

看完这篇博客,面向对象直接拿下,第1张

目录

前言

包的概念

导入包中的类

import关键字

 静态导入

常见的系统包

继承

继承的作用

继承的使用

protect访问权限

protect无法通过父类引用父类对象在不同包使用

super关键字

super的作用

super找到封装属性或方法 

super修饰构造方法

final关键字

多态

向上转型

方法重写

那么如果子类中没有重写方法会怎么样?

如果父类压根没有重写方法会怎么样?

 @Override

 重写与重载的区别:

向上转型类的返回值重写 

向上转型发生的时机

1.方法传参

 2.引用赋值

 3.方法返回值

练习

 向下转型

instanceof关键字

向上转型和向下转型时机总结: 

抽象类

抽象类规定

abstract关键字

子类方法覆写

抽象类引用 

 接口

接口的规定 

接口的应用场景

接口表示一种规范或者标准

接口表示能力 

 接口中的省略


前言

什么是面向对象,说白了,就是把一切问题都转化为对象,比如:狗会游泳 我们就可以把狗转换成对象,把游泳也转化成对象,在Java中,一切问题都源自于对象,都用对象解决。这个时候就会有人觉得:云里雾里,感觉什么都说了又感觉什么都没说,那么我们就带着疑问继续看即可。在我看来,面向对象的概念一定是你学明白他所蕴含的知识才知道面向对象到底表达的是什么。

本篇博客重点介绍面向对象的三大特征以及通过三大特征引申出来的知识。如果你可以把这篇文章吸收好,那么面向对象的概念你也就ok了。

包 包的概念

包其实就是文件夹,在包中创建的包,称之为子包。

在声明一个包时,用关键字package表示,如果嵌套了多个文件夹,就用 " . " 来分隔。

package www.bit.java.rockerclass;

我们可以选中包并且查看文件夹具体位置

 若发现我们建的子包没有显示,而是全部合并到一个文件夹中,那是我们的设置出现了问题

取消合并包即可。

导入包中的类

当我们在写类的时候会发现,同一个包中的类是不可以重复命名的,所以我们可以通过再建一个包的方法来建立一个同样名字的类,这便是包的作用。

那么如何导入包中的类,我们则需要import关键字。

import关键字

import只能用于导入相关包中的具体类,而不是导入整个包,不要混淆这个概念。

此代码说明的是导入www包下的Animal类,一般idea会在产生新对象时自动生成此代码。

import www.Animal;

那么如果我想访问这个包中不止一个类咋办,这个时候可以用 * 这个符号

比如,我想调用工具类,则需要调用工具包,可以用import java.util.*的方式将包中的类按需加载,这个时候导入的还是类,并不是util文件夹。

import java.util.*

 这样调用随之而来会产生的问题是,有可能另一个包中也有相同名称的类,这个时候系统不知道你具体使用的是哪个包中的,就会报错。

这个时候有两种解决方案:

1.使用类的全名称

java.util.Date date = new java.util.Date();
java.sql.Date date1 = new java.sql.Date();

2.在import导入时明确指定是哪个包中的哪个类

import java.util.Date;
 静态导入

import static 可以导入包中的静态方法和静态属性,可以让这个类中认为导入的这个包中的方法和属性是自己写的一样,当我们正常使用静态属性和方法往往采用 类名+方法名/属性名的方式,而静态导入可以直接写方法名和属性名。

package www;
//java.lang这个包下的类在JDK1.1之后默认自动导入(System,String,Object)
//导入System这个类下的所有静态方法和属性
import static java.lang.System.*;
import static java.lang.Math.*;
/**
 * @author DELL
 * @date 2022-04-27 12:01
 */
public class Test {
    public static void main(String[] args) {
        //类名,属性名
        System.out.println(123);
        //当作类是自己写的,直接用
        out.println(123);
        //类名称,方法名称
        int ret = Math.max(15,25);
        int ret1 = max(15,25);
    }
}
常见的系统包

java.lang :JDK的基础类,System,String,Object都在这个包下,JDK1.1之后这个包的所有类自动导入。

java.lang.reflect : 反射开发包

java.util : 工具包(集合类都在这个包下,Arrays,LinkedList,HashMap)

java.io : I/0开发包,文件读取和写入

java.net : 网络编程开发包,Socket

java.sql : 数据库开发用到的包

继承

封装,继承,多态为面向对象三大特征,具体封装可以看我之前的博客:

(4条消息) 类和对象-下篇_小白的含金量的博客-CSDN博客

继承的作用

我们定义一个动物类,Animal,在定义很多的具体动物类,Cat,Dog等等

然后我们分别在类中写同样方法:

pubilc String name;
public void eat(String food) {
        System.out.println(this.name+" eat "+food);
    }

 既然是同样的方法,连代码都一样,我们又何必每个类都写呢,我们把这些动物共有的属性和方法放到同一个类不就好了。所以我们就把这个方法和属性都放在Animal类里面,然后让猫,狗等动物的子类继承这个动物类这个父类就好。子类会继承父类所有的属性和方法。

继承的使用

1.使用关键字extends,并且子类要满足 is a 父类的关系才可以。

2.一个子类只能用extends继承一个父类(单继承),但是可以多层继承

public class Cat extends Animal {

}
public class Animal extends Word {

}

 3.继承分为显式继承(public)和隐式继承(private),隐式继承无法直接使用,可通过父类直接提供的自建方法,或者是getter方法和setter方法使用。

protect访问权限

首先看一波访问修饰符

private < default < protect < public

protect 的权限包括同一个包中的类或者不同包中的有继承关系的类

protected String name;
protect无法通过父类引用父类对象在不同包使用
public class Base {
    protected String name = "图图";
}
//        Base base = new Base();
//        无法通过父类引用使用,在父类外部
//        System.out.println(base.name);
super关键字 super的作用

如果子类和父类存在相同名称的属性或方法,super关键字会略过子类,从直接父类中寻找属性或者方法,如果没找到则继续向上层(爷爷类)寻找。

System.out.println(super.name)
super找到封装属性或方法 

当super找到了父类的private封装的属性或者方法时,会停止寻找,因为在理论上以及找到了父类的属性或方法,但是会报错,不能使用。

super修饰构造方法

在我们使用类并且产生对象的过程中,会产生构造方法,而当我们产生某个子类对象时,会先产生该子类的父类无参构造方法,这是因为,idea默认产生对象并且产生构造方法时,会在子类构造方法的首行产生一行代码:super(),也就是说调用了父类的构造方法。

我们可以自己写出代码尝试练习:

public class D extends B{
    public D() {
        System.out.println("4.D的构造方法--------------");
    }
    {
        System.out.println("5.D的构造块----------------");
    }
    static {
        System.out.println("6.D的静态块----------------");
    }
    public static void main(String[] args) {
        System.out.println("7.main开始。。。。");
        new D();
        new D();
        System.out.println("8.main结束。。。。");
    }
}
public class B {
    public B() {
        System.out.println("1.B的构造方法---------------");
    }
    {
        System.out.println("2.B的构造块-----------------");
    }
    static {
        System.out.println("3.B的静态块-----------------");
    }
}

 上述代码的正确输出是:

3.B的静态块-----------------
6.D的静态块----------------
7.main开始。。。。
2.B的构造块-----------------
1.B的构造方法---------------
5.D的构造块----------------
4.D的构造方法--------------
2.B的构造块-----------------
1.B的构造方法---------------
5.D的构造块----------------
4.D的构造方法--------------
8.main结束。。。。
若我们的父类只存在有参构造方法,则必须在子类构造方法的首行写上super(参数)。

public China(String name) {
        //默认super()调用父类的无参构造 ,若父类没有无参构造必须将super(name)写在构造方法首行
        this();
        System.out.println("china的有参构造");
    }

 这也意味着,在构造方法中,super(参数)和 this()是不可以同时用的,它们都需要写在构造方法的首行,如果父类为无参构造那么this是可以用的,因为默认super()已经写在首行了。

final关键字

final修饰的属性值是不可以变的,是常量。

final修饰的类是无法被继承的。

final class China {
     public final int age = 15;
}
多态

一个引用可以表现出多种行为/特性,则称为多态性

若子类对象表现出的行为特性,完全可以以一个父类来引用它,我们便可以从普通的创建对象便化成向上转型。

Dog dog = new Dog();
Animal animal2 = new Dog();

 那么什么是向上转型呢?

向上转型

向上转型发生在有继承关系的类之间

不一定是子类,孙子类,重孙子类也可以,无穷无尽

Animal animal2 = new Dog();

例如下图这种方法,我们可以看到,在有继承关系的类之间,我们如果每个方法都进行传参时非常麻烦的,并且有代码冗余,那如果我们直接使用父类来指代子类,是否可行。

 我们经过测试,发现是可以的。

 public static void fun(Animal animal) { }

 无论我想传的是子类还是父类,我都可以设置成父类参数来传,同样可以达到效果。

那么如果父类和子类中的方法一样,我是否可以通过父类引用来产生子类的方法,我们发现这样也是可以的,这个,就叫做方法的重写。

方法重写

方法的重写发生在有继承关系的类之间,子类定义了和父类除权限不同(大于父类,并且父类方法为private无法重写),其他全部相同的一组方法。

public class Test {
    // 2.作为类的使用者,程序的使用者
    // 没有向上转型,我要使用fun方法的话,我就得了解Animal以及其子类的所有对象
    // 我才能知道我到底调用的是谁
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Bird();
        Animal animal3 = new Cat();
        Animal animal4 = new Duck();
        fun(animal1);
        fun(animal2);
        fun(animal3);
        fun(animal4);
    }

    // 只要是Animal及其子类,都是天然的Animal对象,都满足is a关系
    // 通过Animal最顶层的父类引用,指代所有的子类对象。
    public static void fun(Animal animal) {
        animal.eat();
    }
}

 这,便是多态性的又一种体现。

那么如果子类中没有重写方法会怎么样?

那么此时会触发就近匹配原则,寻找父类有无重写方法,直到达到顶层。

例如上图中的 Duck类没有重写方法则会就近匹配到他的直接父类Bird

如果父类压根没有重写方法会怎么样?
public class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("Dog类的eat方法");
    }
    public void play() {
        System.out.println("Dog类独有的play方法");
    }

如果我在Dog中创建一个独有的play方法

然后再使用向上继承时,会报错

 因此,我们可以得出结论:

哪个方法可以使用取决于引用的左侧引用的类,而具体用的是哪个方法则看右边产生的类

 @Override

可以查看重写的方法是否正确,若正确不会报错,而且在idea的左侧会有重写成功的标志。

 重写与重载的区别:

向上转型类的返回值重写 

重写的方法返回值可以是向上转型类,例如:

class Person {
    public void fun() {
        this.test();
    }
    public Person test() {
        System.out.println("Person的test方法");
        return new Person();
    }
}

class Student extends Person {
    public Student test() {
        System.out.println("Student的test方法");
        return new Student();
    }
}


public class Test {
    public static void main(String[] args) {
        new Student().fun();
    }
}
向上转型发生的时机 1.方法传参

 2.引用赋值

 3.方法返回值
public static Animal test() {
        Animal dog = new Dog();
        return dog;
    }
练习

产生一个父类Father和一个子类Son,他们进行过方法重写,并且父类的构造方法中调用了此重写方法

public class Father {
    public Father() {
        fun();
    }
    public void fun() {
        System.out.println("我是父类方法");
    }
}
public class Son extends Father{
    private int num = 10;
    public void fun(){
        System.out.println("我是儿子类方法 , num = "+num);
    }

    public static void main(String[] args) {
        Son test = new Son();
    }
}

 那么我在产生对象时,会输出子类方法还是父类方法呢?

可以看出,依然是子类方法,无论我是子类引用直接产生的子类对象,还是采用父类引用向上转型,产生的都是子类方法。因为我new的是子类。 

 向下转型

想要发生向下转型,首先要向上转型。我先在dog中产生一个自己的独有方法play

 public void play() {
        System.out.println("Dog类独有的play方法");
    }

这个时候我要是向上转型想直接用play方法是不可以的,我必须得转回去 

并且由于大转小是强转,所以要写括号,而且有数据丢失的风险。

        Animal animal = new Dog();
        animal.eat();
        // play在Animal中不存在,将animal引用还原为Dog类引用
        Dog dog = (Dog) animal;
        // 再转回去
        Animal animal1 = dog;
        dog.play();
        // 此时这个animal引用和Dog类毫无关系

向下转型的应用场景为,当我想要使用子类独有的方法,比如dog中独有的play方法,我便需要向上转型。

instanceof关键字

我们用instanceof关键字来判断该引用本质是不是该类对象,能否进行向下转型

        Animal animal1 = new Animal();
        Animal animal2 = new Dog();
        if (animal1 instanceof Dog) {
            Dog dog = (Dog) animal1;
            System.out.println(animal1 + "转型成功");
        } else {
            System.out.println(animal1 + "不是指向Dog类型引用");
        }
        if (animal2 instanceof Dog) {
            Dog dog = (Dog) animal2;
            System.out.println(animal2 + "转型成功");
        } else {
            System.out.println(animal2 + "不是指向Dog类型引用");
        }
向上转型和向下转型时机总结: 

向上转型:当一个方法接收一个类和当前子类,参数指定为相应的父亲引用,发生的就是向上转型。

向下转型:当需要使用子类拓展方法,才需要向下转型为子类引用。

抽象类

向上转型的好处:参数统一化,用一个共同的父类引用,可以接受所有子类实例。

向上转型的应用类,比如一个描述形状的类Sharp,子类是三角形,正方形,圆形等。

那么我们就可以把Sharp设定为抽象类。

抽象类无法具体到某一个实例,是一个概念化的类

抽象类规定

抽象类是普通类的“超集”,只比普通类多了抽象方法。

其余的一切,与普通继承一样

包括:满足原则,构造方法由父到子等等

abstract关键字

抽象方法所在的类只能是抽象类,抽象类和抽象方法用关键字abstract声明

因为抽象方法中无具体函数实现的方法,所以没有方法体

abstract class A {
    abstract void printA();
}

ps:当一个题中说没有方法体的方法就是抽象方法,那么这句话肯定是错的,必须是abstract定义的才是。

子类方法覆写

 继承抽象类的普通子类必须覆写所有的抽象类方法

而继承抽象类的抽象子类可以选择覆写抽象方法

abstract class A {
    abstract void printA();
}

abstract class B extends A {
    abstract void printB();
}

public class C extends B{
    @Override
    void printA() {}
    @Override
    void printB() {}
}
抽象类引用 

抽象类不可产生自己的引用,只能通过普通子类向上转型

 接口

可以当作是一种抽象类的进化版,当一个需求既可以用接口又可以用抽象类,先选择用接口。

接口的规定 

接口中只有抽象方法和全局常量,是更加纯粹的抽象概念。

接口使用关键字interface声明接口

public interface USB {
    //插入
    public abstract void plugIn();
    //工作
    public abstract void work();
}

子类使用implements实现接口

public class KeyBoard implements USB{
    @Override
    public void plugIn() {
        System.out.println("安装键盘驱动中~");
    }
    @Override
    public void work() {
        System.out.println("键盘正常工作了~");
    }
}
接口的应用场景

1.接口表示具备某种能力/行为

2.接口表示一种规范或者标准

接口表示一种规范或者标准

我们拿USB接口举例,鼠标,键盘外设都属于USB接口的子类

public interface USB {
    //插入
    public abstract void plugIn();
    //工作
    public abstract void work();
}

 那么子类用implements来实现,并且需要覆写所有抽象方法

public class KeyBoard implements USB{
    @Override
    public void plugIn() {
        System.out.println("安装键盘驱动中~");
    }
    @Override
    public void work() {
        System.out.println("键盘正常工作了~");
    }
}
public class Mouse implements USB{
    @Override
    public void plugIn() {
        System.out.println("安装鼠标驱动中~");
    }
    @Override
    public void work() {
        System.out.println("鼠标正常工作了~");
    }
}

 Computer类作为USB接口的使用者,并不关心插到接口上的到底是啥外设,所以我们定义方法时,直接传入接口类USB的参数,这样无论我到底插的是鼠标还是键盘,都可以识别。

public class Computer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Mouse mouse = new Mouse();
        // 插入鼠标 ok
        computer.fun(mouse);
        KeyBoard keyBoard = new KeyBoard();
        // 插入键盘 ok
        computer.fun(keyBoard);
    }
    // 此时为何方法的参数用的是USB接口引用?
    // fun方法就模拟咱电脑的USB的插口
    // 假设咱们fun方法参数Mouse会咋样?
    public void fun(USB usb) {
        usb.plugIn();
        usb.work();
    }
}

 如果我还想增加外设,那么再增加一个新的外设类即可,不必改变Computer类的任何值。

Camera camera = new Camera();
computer.fun(camera);
接口表示能力 

 接口是允许多实现的,也就是说一个子类可以实现多个父类接口

实现多个接口时用逗号 " , " 间隔开

public class Rabbit implements ISWim,IRun {
    public void swim() {
        System.out.println(test);
        System.out.println("兔子在游泳");
    }

    public void run() {
        System.out.println("兔子在跑");
    }
}

这样当我使用接口时,会跑的可以是兔子,会游泳的也可以是兔子。

public class Test {
    public static void main(String[] args) {
        IRun rabbit = new Rabbit();
        ISWim rabbit1 = new Rabbit();
        rabbit.run();
        rabbit1.swim();
    }
}
 接口中的省略

因为接口中只有全局常量和抽象方法,所以:

public abstract 可以省略

static final  也可以省略

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/796261.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存