【JAVA】类和对象,继承,多态,抽象类,接口

❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

什么是类,对象

因为计算机并不能像我们人这样去理解世界上的东西,所以为了更好的编辑,就抽象出了类和对象。类就是把功能放在一起,然后由一个人去调用这个功能,然后再编辑对应的功能。调用者就是对象的实现者

类和类的实例化

类是一类对象的统称,对象就是这一类具体化的实例
创建类的关键字:class
举例:我们坐年糕的模子就是一个类,而通过这个模子可以做出月饼。在这个例子当中,类就是模具,年糕就是实例化的对象。一个类可以实例化很多对象。举例:

class Person {
    public String name;
    public int age;
}
public static void main1(String[] args) {
    Person person1 = new Person();
    Person person2 = new Person();
    Person person3 = null;
}

Person 就是一个类,里面的 name 和 int 就是类的成员(字段)。这里的 person1 就是引用,指向的是后面 new 的对象,因为是引用,所以也可以指向 null 。person3 就是指向空(null)引用。person1 person2 person3 就是实例化的对象。

字段的初始化

字段就是类里面的成员。在创建这些成员的时候就可以初始化。例如:

class Person {
    public String name = "Lockey";
    public int age = 10;
}

类中实现方法

class Person {
    public String name;
    public int age;
    public void print() {
        System.out.println("姓名:"+name+" 年龄:"+age);
    }
}
public static void main(String[] args) {
    Person person1 = new Person();
    person1.name = "Lockey";
    person1.age = 10;
    person1.print();
}

static 静态关键字

static 可以修饰方法,属性。要注意的是:static 修饰的变量通过类名访问

class Person {
    public int a;
    public static int count;
}
public static void main(String[] args) {
    Person person1 = new Person();
    person1.a++;
    person1.count++;
    System.out.println(person1.a);
    System.out.println(Person.count);
    Person person2 = new Person();
    person2.a++;
    person2.count++;
    System.out.println(person2.a);
    System.out.println(person2.count);
}

在这里插入图片描述
如图,count 第二次输出为 2 。这是因为 conut 是被 static 修饰的变量,被 static 修饰之后就变成了常量,就在静态区了。所以每个引用当中调用的 count 没变。所以第二次改变 count 的值的时候,就变成 2 了。当 static 修饰方法的时候,无需再创建对象,直接拿类名调用就行了。举例:

class Person {
    public static int count;
    public static void change() {
        count = 100;
    }
}
public static void main(String[] args) {
    Person.change();
    System.out.println(Person.count);
}

在这里插入图片描述
通过类名的调用直接访问 static 修饰的变量。

封装

因为代码实现的软件很复杂,所以就出现了类但是为了阅读性更高,就出现了封装。就是把一个类的内容实现为只剩一个或几个接口,方便类的调用者访问,这样就降低了代码的复杂程度

private 封装

private 就是修饰成员变量或方法的关键字,被 private 修饰之后,就不能被类外的调用者访问了。所以就可以配合 public 来实现对外的接口。例如:

class Person {
    private String name = "Lockey";
    private int age = 10;
    public void print() {
        System.out.println("名字是:"+name+" 年龄:"+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;
    }
}
class test {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.getAge());
        person.print();
    }
}

在这里插入图片描述
这里就是利用 private 封装属性,使其只能在类内被访问到。而提供的 public 接口,就让类的调用者在类外就可以访问到了。所以在类外直接调用 print 方法就好了。

setter 和 getter 方法

当我们把字段(name age)设置为 private 属性时,就无法在类外进行初始化了,所以就又出现了新的方法:

class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name1) {
        name = name1;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age1) {
        age = age1;
    }
    public void print() {
        System.out.println("名字是:"+name+" 年龄:"+age);
    }
}
public static void main(String[] args) {
    Person person = new Person();
    person.setName("Lockey");
    person.getName();
    person.setAge(10);
    person.getAge();
    person.print();
}

在这里插入图片描述
这里就是调用 getter 和 setter 方法,在类外进行访问,因为 private 对 name 和 age 做了封装。

构造方法

方法名和类名是相同的,且构造方法没有返回值调用完构造方法之后,对象才会产生。

一个对象的产生:
1 为对象分配内存。
2 调用合适的构造方法。

class Person {
    public Person() {
        System.out.println("Person 不带参数的构造方法");
    }
}
public static void main(String[] args) {
    Person person = new Person();
}

在这里插入图片描述
这里就是实例化类的对象的时候调用的构造方法,因为我们自己写了构造方法,所以这里就调用我i们写过的。就输出了如图所示的内容。需要注意的是:如果没有实现如何的构造方法,编译器会帮我们默认生成有个不带参数的构造方法。也就是说,一个类,至少会有一个构造方法。
除了上面这种构造方法,我们还能写带有参数的构造方法。例如:

class Person {
    private String name;
    public Person(String name1) {
        name = name1;
        System.out.println("Person(String) 带一个参数的构造方法");
    }
}
public static void main(String[] args) {
    Person person = new Person("Lockey");
}

在这里插入图片描述
如图:在类初始化的时候,传入参数 “Lockey” 在类实例化的时候就是调用有参数的构造方法。既然能传参数,那是否可以传多个参数呢?答案是可以的。例如:

class Person {
    private String name;
    private int age;
    public Person(String name1, int age1) {
        name = name1;
        age = age1;
        System.out.println("Person(String, int) 带两个参数的构造方法");
    }
}
public static void main(String[] args) {
    Person person = new Person("Lockey",10);
}

在这里插入图片描述
如图,调用的就是传了两个参数的构造方法。通过观察上面的例子,我们可以发现:构造方法也是支持重载的。

this关键字

在类当中,this 表示当前对象的引用(不是当前对象),可以通过 this 来访问类当中的字段和方法。用 this 的话,会降低出现 bug 的概率,并且增加代码的可读性。例如使用 getter 和 setter 的时候,就可以用 this ,能多用 this 就多用 this 。

class Person {
    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;
    }
}

继承

在创建的类当中,是为了给一些抽象的事物提供一些方法。如果一些类、一些食物有共同的属性,那么就可以把这些属性封装为一个类。然后其它事物有这个属性的时候,就去继承这个类。通过 extends 关键字去继承。就比如说:猫和鸟,它们的共同特点都有名字和吃。所以就可以把名字和吃放在一个类里面。然后让它们去继承,降低代码量。把被继承的这个类叫做 Animal 可以发现猫和鸟对于 Animal 都是一种 is-a 的关系。在继承的时候,也会继承父类(Animal)的字段和方法。

class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}
class Cat extends Animal {
    public Cat(String name) {
        // 使用 super 调用父类的构造方法.
        super(name);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void fly() {
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
    }
}
public class Test4 {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑");
        cat.eat("猫粮");
        Bird bird = new Bird("圆圆");
        bird.fly();
    }
}

子类在调用父类的构造方法的时候,要用到 super 关键字。用 super 关键字去把参数传进去。运行结果如下:
在这里插入图片描述
bird 就是在 Animal 继承的基础上又扩展出了 fly 方法。
如果把 name 的 public 改成 private 那么子类就访问不到了:

class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}

在这里插入图片描述

protected 关键字

因为把访问权限设置为 private 就会导致子类不能访问。如果是 public 就会导致失去封装的意义。所以就可以使用 protected 。因为对于类的子类和同一个包的其他类来说,protected 修饰的字段是可以访问的。

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}
class Cat extends Animal {
    public Cat(String name) {
        // 使用 super 调用父类的构造方法.
        super(name);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void fly() {
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
    }
}
public class Test4 {
    public static void main(String[] args) {
        System.out.println("保护权限下的访问");
        Cat cat = new Cat("小黑");
        cat.eat("猫粮");
        Bird bird = new Bird("圆圆");
        bird.fly();
    }
}

在这里插入图片描述

java对于字段的方法的四种访问权限

private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问

在这里插入图片描述

final关键字

之前学过,用 final 来修饰常数的时候,这个数不能被修改。所以用 final 来修饰类的时候,此时被修饰的类就不能被继承

final class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}
class Cat extends Animal {
    public Cat(String name) {
        // 使用 super 调用父类的构造方法.
        super(name);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void fly() {
        System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
    }
}
public class Test4 {
    public static void main(String[] args) {
        Cat cat = new Cat("小黑");
        cat.eat("猫粮");
        Bird bird = new Bird("圆圆");
        bird.fly();
    }
}

在这里插入图片描述

多态

向上转型

在上一篇 包和继承 当中我们写继承的关系的时候,写了这样的代码:

Bird bird = new Bird("圆圆"); 

当然,我们也可以写成这样:

Bird bird = new Bird("圆圆"); 
Animal bird2 = bird; 

或者下面这样:

Animal bird2 = new Bird("圆圆"); 

此时 bird2 就是一个父类(Animal)的引用,指向一个子类(Bird)的实例,这就是向上转型。就像可以把:你给笔记本电脑充电了吗?说成:你给电脑充电了吗? 因为笔记本电脑也是电脑。
为什么叫向上转型:因为在程序设计当中,有很多场景。为了表示这种关系,就可以把他们的关系图画出来,父类通常在子类的上方。所以就叫做向上转型,表示往父类的方向转。

方法重写

子类实现了父类的同名方法,并且参数的类型和个数完全相同,就称之为:覆写/重写/覆盖

重写和重载完全不一样.。
普通方法可以重写, static 修饰的静态方法不能重写
重写中子类的方法的访问权限不能低于父类的方法访问权限。
重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外)。

如果我们把子类当中的权限改成比父类小的话,就会报错。代码如下:

class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("这里是动物");
        System.out.println(this.name + "正在吃" + food);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    private void eat(String food) {
        System.out.println("这里是小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}
public class Test4 {
    public static void main(String[] args) {
        Animal animal1 = new Animal("圆圆");
        animal1.eat("谷子");
        Animal animal2 = new Bird("扁扁");
        animal2.eat("谷子");
    }
}

在这里插入图片描述

重写和重载的区别

在这里插入图片描述

理解多态

学了向上转型,动态绑定,方法重写之后,我们就可以使用多态的形式来设计程序了,写一些弗雷可以兼容子类的代码。例如,打印多种形状:

class Shape {
    public void draw(){
        System.out.println("Shape::draw()");
    }
}
class Rect extends Shape{
    public void draw() {
        System.out.println("♦");
    }
}
class Flower extends Shape{
    //alt + insert 快捷键输出重写的方法
    @Override
    public void draw() {
        System.out.println("❀");
    }
}
class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("🔺");
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}
public class Test {
    // 打印单个图形
    public static void drawShape(Shape shape) {
        shape.draw();
    }
    public static void main(String[] args) {
        Shape shape1 = new Flower();
        Shape shape2 = new Cycle();
        Shape shape3 = new Rect();
        drawMap(shape1);
        drawMap(shape2);
        drawMap(shape3);
    }
}

在这里插入图片描述
这里类的代码是由类的实现者写的,main 函数是由类的调用者写的。当类的调用者在写 drawMap 方法的时候,就会通过父类去看一个调用那个子类的方法。

多态的好处

1.进一步的封装,让类的调用者不知道类的细节
2.降低代码量,避免使用大量的if else
3.可扩展能力更强

super关键字

前面的代码调用的是子类的方法,如果要调用父类的方法就要用到 super 关键字。
1.使用了 super 来调用父类的构造器:

public Bird(String name) {
    super(name);
}

2.使用 super 关键字来调用父类的普通方法:

class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    @Override
    public void eat(String food) {
        //在这里让子类调用父类的接口
        super.eat(food);
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}
public class test5 {
    public static void main(String[] args) {
            Bird bird = new Bird("xxn");
            bird.eat("ee");
    }
}

在这里插入图片描述

super和this的区别

在这里插入图片描述

抽象类

就像这个代码:

class Shape {
    public void draw(){
        System.out.println("Shape::draw()");
    }
}
class Rect extends Shape{
    public void draw() {
        System.out.println("♦");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}
class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("🔺");
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}
public class Test {
    public static void main(String[] args) {
        Rect rect = new Rect();
        Flower flower = new Flower();
        Triangle triangle = new Triangle();
        Shape[] shapes = {triangle,rect,triangle,rect,flower,new Cycle()};
        for (Shape s: shapes) {
            s.draw();
        }
    }
}

在这个打印图形的例子当中,可以看到父类的 shape 方法并没有实际作用,主要的工作都由子类完成了,像这些没有实际工作的方法,我们就可以把它设计成一个抽象方法,包含抽象方法的类叫做抽象类:

abstract class Shape{
    public abstract void draw();
}

在 draw 方法前面加上 abstract 关键字就变成了抽象方法,但是包含抽象方法的类,必须用 abstract 修饰。抽象方法不用具体实现,抽象类就是Shape,然后Shape下面有很多的具体实现的子类

使用抽象类的注意事项

1.抽象类不能直接实例化

public static void main(String[] args) {
    Shape shape = new Shape();
}

在这里插入图片描述
2.抽象方法是不能用 private 修饰的

abstract class Shape{
    private abstract void draw();
}

在这里插入图片描述
3.抽象类中可以包含其他的非抽象方法,也可以包含字段,这里的非抽象方法和普通方法是一样的,可以被重写,也可以被子类直接调用,但是一个普通类要继承抽象类,那么必须重写抽象类当中的所有抽象方法。

abstract class Shape{
    public void func() {
        System.out.println("func");
    }
    public abstract void draw();}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦"+a);
        super.func();
    }
}
public class Test {
    public static void drawMap(Shape shape) {
        shape.draw();
    }
    public static void main(String[] args) {
        Shape shape = new Rect();
        shape.func();
    }
}

抽象类的作用

抽象类存在的最大意义就是为了被继承,抽象类本身并不能被实例化,要想使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。

接口

接口是抽象类的更进一步抽象类中还可以包含非抽象方法和字段。而接口中包含的方法都是抽象方法,字段只能包含静态常量。
在上面的打印图像代码当中,父类 shape 并没有包含别的抽象方法,所以就可以设计成一个接口。代码示例:

interface IShape {
    void draw();
}
class Cycle implements IShape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
public class Test5 {
    public static void main(String[] args) {
        IShape shape = new Cycle();
        shape.draw();
    }
}

在这里插入图片描述
这里应该注意的是:

使用接口的时候,要用 interface 定义。
接口中的方法一定是 public ,因此可以省略 public 。
接口中的方法一定是抽象方法,因此可以省略 abstract 。
Cycle 使用 implements 继承接口,此时表达的含义不再是“扩展”,而是“实现”。
静态常量代码当中的 public static final 关键字都可以省略
接口不能单独被实例化。

接口当中只能包含抽象方法,对于字段来说,接口中只能包含静态常量:

interface IShape {
    void draw();
    public static final int num = 10;
}

实现多个接口

有些时候我们需要让一个类同时继承多个父类,但是 Java 实现不了多继承。不过可以通过同时实现多个接口来达到多继承类似的效果。通过类来表示一组动物(通过接口来调用就不用关心引用是谁了):

class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

}
interface IFlying {
    void fly();
}
interface IRunning{
    void run();
}
interface ISwimming{
    void swimming();
}
class Bird extends Animal implements IFlying{
    public Bird(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name+"正在飞");
    }
}
class Frog extends Animal implements IRunning,ISwimming{
    public Frog(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在跑");
    }
    @Override
    public void swimming() {
        System.out.println(this.name+"在游泳");
    }
}
class Duck extends Animal implements IRunning,ISwimming,IFlying{
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name+"正在飞");
    }
    @Override
    public void run() {
        System.out.println((this.name+"正在跑"));
    }
    @Override
    public void swimming() {
        System.out.println(this.name+"在游泳");
    }
}
class Roobot implements IRunning{
    @Override
    public void run() {
        System.out.println("机器人在跑");
    }
}
public class Test4 {
    public static void runFunc(IRunning iRunning){
        iRunning.run();
    }
    public static void swimmingFunc(ISwimming iSwimming){
        iSwimming.swimming();
    }
    public static void flyingFunc(IFlying iFlying){
        iFlying.fly();
    }

    public static void main(String[] args) {
        runFunc(new Duck("鸭子"));
        runFunc(new Frog("青蛙"));
        runFunc(new Roobot());
    }
}

在这里插入图片描述

接口间的继承

接口也可以继承一个接口(也叫扩展),达到复用代码的效果。也是使用 extends 关键字:

interface IA1 {
    void funcA();
}
interface IB1 extends IA1{
    void funcB();
}
//因为要拓展接口的功能 所以有 B 和 A 所以这俩的方法都有重写
class C implements IB1{
    @Override
    public void funcB() {
    }
    @Override
    public void funcA() {
    }
}


♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李小浦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值