编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

别再说你不懂java面向对象了,阿里P7大佬一次性给你讲的明明白白

gowuye 2024-04-04 11:52 12 浏览 0 评论

前言

面向对象在百度百科中是这样解释的:“面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物”。说的好像很流弊的样子,看看就行。

包的认识

1.1:包的概念包是组织、整合类的一种方式其目的在于保证使用类的唯一性(同一个包中,里面所包含的类名是唯一的)比如在包A中定义了一个Test类,在包B中也定义了一个Test类,那么当使用A中的Test类时便导入包A调用A中的Test类(A.Test),以此保证类的唯一性。

1.2:导入包中的类Java中有很多现成的包供我们使用,使用这些包的方式有两种

public class TestDemo {
    public static void main(String[] args) {
        java.util.Date time = new java.util.Date();
        //使用 util 包中的Date类创建一个对象
        System.out.println(time.getTime());
        //调用该对象的 getTime 方法,获得一个毫秒级别的时间戳
    }
}

上述代码中,util 就是一个包,Date就是该包中的一个类,使用时用 java.包名.类名 使用

import java.util.Date;
public class TestDemo {
    public static void main(String[] args) {
        Date time = new Date();
        System.out.println(time.getTime());
    }

第一个代码每次使用类时都要加包的前缀,太过麻烦,因此还可使用第二种方式,在代码最开头使用 import 关键字导入需要的包,这样就可以像使用自己定义的类一样,直接用类名创建对象当同时需要同一个包中的多个类是,可以用 import.java.包名.* 的方式导入包,这样就可以使用该包下的所有类(但建议不要这么使用,否则当导入多个包时还是会出现类名重复的现象)。

1.3:静态导入包中除了类的普通方法外,还有一些静态方法,使用这些方法时,可以使用 import static 的方式导入包中的静态字段和方法,无需创建对象直接使用

import static java.lang.Math.*;
public class TestDemo {
    public static void main(String[] args) {
        double x = 10.5;
        double y = 3.0;
        double result = sqrt(pow(x,y)+pow(x,y));
        System.out.println(result);
    }

1.4:自定义类放入包中基本规则

  • 在文件的最上方加一个 package 语句指定该代码在哪个包中
  • 包名尽量指定成唯一名字,防止多文件间冲突
  • 包名和代码路径相匹配
  • 如果一个类没有 package 语句,则该类会被放到默认包中

1.5:包的访问权限控制如果某个成员不包含 public 和 private 关键字,此时这个成员可以被包中的其他类使用,但不能在包外部类中使用

1.6:常见的系统包1、java.lang:系统常用基础类(String,Object),jdk1.1以后自动导入2、java.lang.reflect: java 反射编程包3、java.net:进行网络编程开发包4、java.sql:进行数据库开发的支持包。5、java.util:是java提供的工具程序包。(集合类等)6、java.io:I/O编程开发包。

继承

2.1 概念:当创建多个类时发现这些类之间存在一定的关联关系,则可以创建一个父类拥有这些类的相同属性和方法,让这些类继承与父类,来提高效率,达到代码重用的效果。此时被继承的类我们成为 父类、基类或者超类,而继承的类我们称为子类或者派生类,子类会继承父类除构造方法外的其他所有东西。2.2 语法规则:class 子类 extend 父类{

}

  • 子类使用 extend 关键字继承父类
  • Java中一个子类只能继承一个父类
  • 子类会继承父类的所有 public 的字段和方法
  • 对于父类的 private 的字段和方法,子类无法访问
  • 子类实例中也包含父类实例,可使用 super 关键字得到父类的引用
class Animal{
    public String name;//父类的name必须为 public 否则子类将无法访问
    public Animal(String name) {
        this.name = name;
    }

    public void eat(String food){
        System.out.println(this.name+"吃"+food);
    }
}
class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
}

class Bird extends Animal{
    public Bird(String name){
        super(name);
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("旺旺");
        dog.eat("骨头");
        Bird bird = new Bird("啾啾");
        bird.eat("米粒");
        bird.fly();
    }

2.3、protected关键字上述代码中,如果将父类的 name 设为 private子类将无法访问,但设为 public 又违背了封装的规则,此时就需要用到 protect 关键字

  • 对于其他类的调用者来说,被 protect 修饰的字段和方法是不能被访问的
  • 但对于 该类的子类同一个包下的其他类 来说,被 protect 修饰的字段和方法是可以被访问的

小结:Java中对字段和方法共有四种访问权限

  • private:只能在类内部访问,内外部一律不行
  • 默认:在类内部能访问,同一个包中的类也可以访问
  • protect:在类内部能访问,且在其子类和同一个包中的类也可以访问
  • public:类内部和类的调用者都能访问

2.4、final关键字final 可以修饰一个变量或者字段,用来表示常量final 也恶意修饰一个类,用来表现该类不能被继承组合:即一个类中对另一个类的嵌套,在一个类中实例另一个类,达到代码重用的效果

多态

3.1、向上转型:如上代码,创建一个 Bird 的对象可以写成

Bird bird = new Bird();

也可以写成

Bird bird = new Bird();
Animal bird2 = bird;
Animal bird3 = new Bird();

此时bird2、bird3是一个父类(Animal)的引用,指向子类(bird)的实例,这称为向上转型。向上转型发生的时机:

  • 直接赋值(即上述代码演示)
  • 方法传参
  • 方法返回

方法传参:

public static void main(String[] args) {
    Bird bird = new Bird("啾啾");
    feed(bird);
}
public static void feed(Animal animal){
    animal.eat("谷子");
}

此时形参的类型是 Animal(父类) ,实际上是对应到 Bird(子类)的实例

方法返回:

public static void main(String[] args) {
    findAnimal().eat("谷子");
}
public static Animal findAnimal(){
    Bird bird = new Bird("啾啾");
    return bird;
}

方法 findAnimal 返回值是 Animal 的一个引用,但实际指向 Bird 的一个实例

3.2:动态绑定子类和父类出现同名方法时发生

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);
    }
    public void eat(String food){
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Animal animal1 = new Animal("动物");
        Animal animal2 = new Bird("小鸟");
        animal1.eat("谷子");
        animal2.eat("谷子");
    }

执行以上代码我们发现:

  • animal1 和 animal2 虽然都是 Animal 类型的引用,但animal1指向 Animal 实例的对象,animal2指向 Bird 实例的对象
  • 对 animal1 和 animal2 分别调用eat方法,发现 animal1 调用的是父类的方法,animal2 调用的是子类的方法

在Java中,调用某个方法,究竟执行哪段代码(是父类方法还是子类),取决于看这个引用指向的对象是父类对象还是子类对象,这个过程是在运行时才决定的,因此成为动态绑定父类引用,引用子类对象时,只能访问自己特有的,不能访问子类独有的

3.3、重写对于上述代码,父类和子类的eat方法来说,就构成了重写重写的注意事项:

  • 普通方法可以重写,static 修饰的方法不能重写
  • 重写中子类的访问权限不能低于父类的访问权限
  • 重写和重载不同

重写和重载的区别

1.要求不同重载:a.方法名相同 b.参数列表不同 c.返回值不做要求重写:a.方法名相同 b.参数列表相同(参数类型和个数) c.返回值也要相同2.范围不同重载:同一个类中重写:继承关系,不同类中3.限制重载:无限制重写:子类中重写的方法访问权限不能低于父类的访问权限

3.4、理解多态

class Shape {
    public void draw(){

    }
}
class Circle extends Shape{
    public void draw(){
        System.out.println("画一个圆圈○");
    }
}
class Rectangle extends Shape{
    public void draw(){
        System.out.println("画一个矩形□");
    }
}
class Triangle extends Shape{
    public void draw(){
        System.out.println("画一个三角△");
    }
}
//================================================
//================================================
public class TestDemo {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();
        Shape shape3 = new Triangle();
        drawMap(shape1);
        drawMap(shape2);
        drawMap(shape3);
    }
    public static void drawMap(Shape shape){
        shape.draw();
    }

以上代码,分割线以上是由类的实现者编写的,分割线以下是由类的调用者编写的当调用者编写drawMap方法的时候(参数类型为父类Shape),并不关心当前shape引用指向哪个类型的实例,此时shape调用的draw方法只取决于shape指向的哪个类型的实例,以实现不同的表现形式,这种思想叫做多态。

3.5、向下转型父类引用,引用子类对象称为向上转型;子类引用,引用父类对象称为向下转型。

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);
    }
}

ass Bird extends Animal{
    public Bird(String name){
        super(name);
    }
    public void eat(String food){
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}
    public class TestDemo {
    public static void main(String[] args) {
        Animal animal = new Bird("啾啾");
        animal.eat("谷子");
        animal.fly();//执行该行代码时,会报错误
    }

编译过程中 animal 的类型时 Animal ,编译器编译时根据类型只知道 animal 中只有一个eat方法,找不到fly方法

  • 编译器检查存在哪些方法时候,看的是 Animal 类型
  • 执行时究竟执行父类方法还是子类方法,看的是 Bird 类型
    但很多时候为了让向下转型更安全,需要判断一下 animal 是否指向 Bird 的引用,使用instanceof 关键字判断
Animal animal = new Cat("小猫");
if (animal instanceof Bird) {
Bird bird = (Bird)animal;
bird.fly();
}

3.6、super关键字当在子类内部要调用父类方法时,就要用到 super 关键字super 表示获取到父类实例的引用,其有两种常用方法

一、使用 super 来调用父类的构造方法,super(参数)

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

二、使用 super 来调用父类的普通方法,super.方法名(参数)

class Bird extends Animal{
    public Bird(String name){
        super(name);
    }
    public void eat(String food){
        super.eat(food);
        System.out.println("我是一只小鸟");
        System.out.println(this.name+"正在吃"+food);
    }
}

上述代码中个,如果直接调用eat方法,则被编译器认为是调用子类的方法(同递归),想要调用父类方法则需要使用 super 关键字

this和super的区别:

1、概念this:访问本类的属性和方法super:访问父类的属性和方法2、查找范围this:先查找本类,子类没有再调用父类super:不查找本类,直接调用父类3、特殊this:表示当前对象的引用super:表现父类对象的引用

多态存在的意义就在于,让调用者不必关注对象的具体类型,降低用户的使用成本

4、抽象类

4.1语法规则类似于之前代码的父类 Shape 中的draw方法,其中并没有实际工作,而由其子类重新该方法实现,那么像这种没有实际工作的方法我们就可以用 abstract 关键字修饰把它设计成一个抽象方法,而包含抽象方法的类我们就称为抽象类

abstract class Shape {
   abstract public void draw();
}
  • 在draw方法前加一个 abstract 修饰表示该方法是抽象方法,抽象方法没有方法体
  • 对于包含抽象方法的类,class 前必须加一个 abstract 关键字表示是抽象类

注意事项:

  • 抽象类不能直接实例
  • 抽象方法的访问权限不能是private
  • 抽象类中可以包含其他非抽象方法,也可以包含字段,和普通方法一样,可以被重新可以被调用

4.2抽象类的作用抽象类的意义在于为了被继承抽象类本身不能实例化,想要使用,必须创建该抽象类的子类,在子类中重写抽象方法使用抽象类相当于多了一重编译器的检验(对于抽象了来说,如果继承的子类不重写父类的抽象方法,则会报错)

5、接口

接口是比抽象类还抽象,接口只包含抽象方法,其字段也只能包含静态常量

interface IShape{
    abstract public void draw();
}

class Circle implements Shape{
    @Override
    public void draw() {
        System.out.println();
    }
}
  • 用 interface 定义一个接口
  • 接口中的方法只能是抽象方法,所以可以省略 abstract
  • 并且接口中的方法只能是 public ,所以可以省略
  • Circle 用 implement 来实现接口
  • 在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例
  • 接口不能被单独实例化

在Java中一个类只能继承一个父类,但同时可以实现多个接口

class Animal{
    public String name;
    public Animal(String name){
        this.name = name;
    }
}
interface IFlying{
    void fly();
}
interface IRunning{
    void run();
}
interface ISwimming{
    void swim();
}
class Dog extends Animal implements IRunning{
    public Dog(String name){
        super(name);
    }
    @Override
    public void run() {
        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 swim() {
        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 swim() {
        System.out.println(this.name+"正在游泳");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("汪汪");
        Frog frog = new Frog("呱呱");
        Duck duck = new Duck("嘎嘎");
        dog.run();
        frog.run();
        frog.swim();
        duck.fly();
        duck.run();
    }
}

接口使用实例——给对象数组排序创建一个学生类

class Students implements Comparable{
    private String name;
    private int score;
    public Students(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        Students s = (Students)o;
        if(s.score > ((Students) o).score){
            return 1;
        }else if(s.score == ((Students) o).score){
            return 0;
        }else{
            return -1;
        }
    }
}

在 sort 方法中会自动调用 compareTo 方法.compareTo 的参数是 Object,其实传入的就是Students类型的对象,然后比较当前对象和参数对象的大小关系

接口间的承接一个接口可以承接另一个接口,达到复用效果,使用 extends 关键字接口间承接相当于把多个接口凭借在一起

Clonable 接口和深拷贝Object 类中存在一个 clone 方法,调用这个方法可以创建一个对象的“拷贝”,但是想要合法的调用clone方法,必须先要实现 Clonable 接口,否则就会抛出 CloneNotSupportedException 异常

class Person implements Cloneable{
    public String name;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class TestDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("小明");
        Person person2 = (Person) person1.clone();
        person2.name = "小红";
        System.out.println(person1);
        System.out.println(person2);
    }
}

如下代码即深拷贝:

class Money implements Cloneable{
    public int m=10;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable{
    public String name;
    public Money money ;

    public Person(String name) {
        this.name = name;
        this.money = new Money();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.money = (Money) this.money.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", money=" + money.m +
                '}';
    }
}

public class TestDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("小明");
        Person person2 = (Person) person1.clone();
        person2.name = "小红";
        person2.money.m = 15;
        System.out.println(person1);
        System.out.println(person2);
    }
}

总结

  • 抽象类和接口都是Java中多态的常见使用方法,但两者又有区别
    最核心区别:抽象类可以包含普通方法和普通字段,可以被子类直接使用,而接口中只有抽象方法,实现该接口的类必须重写所有抽象方法

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

相关推荐

爱上开源之golang入门至实战第四章-切片(Slice)

前言Go数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可...

Go语言入门必知教程-切片

切片是一种灵活的和可扩展的数据结构,用于实现和管理数据集。切片由多个元素组成,所有元素都是相同类型的。切片是动态数组的一部分,可以根据需要进行增长和收缩。与数组一样,切片也可以索引。切片具有容量和长度...

Go语言基础-切片

切片是什么?切片是Go语言的一种数据结构。和数组相似,不过切片可以在它的结尾增加更多的元素。这样可变长度在实际编程中更为有用。声明切片切片的声明和数组也很相似,只是声明切片时不需要指定大小。例:va...

5分钟掌握GO中切片的基本使用方法

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

揭秘 Go 切片(Slice)的秘密

当向切片添加新参数时,底层数组会发生什么变化?它会扩展以容纳更多元素吗?在这篇文章中,我们将深入探讨切片的内部工作原理,以及如何利用这些知识来进行更好的内存管理和性能优化。具体而言,我们将探索Go...

【Go语言slice详解】深入掌握Go语言中的slice类型及常用操作!

Go语言中的slice(切片)是一种非常方便的数据结构,可以动态地增加或减少其元素数量,且可以访问底层数组的任意一个子序列。本文将对Go语言中的slice进行详细的讲解。Slice的定义在Go语言中,...

掌握GO中的Slice,这就够了

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

golang2021面向对象(26)Go语言类型内嵌和结构体内嵌

结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体。?可以粗略地将这个...

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内的指针方法列表?以下代码应该返回{"S1":["M1","M2"],"S...

Go语言文件和目录操作

文件和目录操作概述一、文件和目录操作概述在计算机中,文件和目录是存储数据的重要方式。在Go语言中,我们可以使用os和io/ioutil包提供的函数和结构体来进行文件和目录操作。本文将详细介绍Go语言中...

跟我一起学习go语言(五)golang中结构体的初始化方法

1、自定义一个结构体typeVertexstruct{X,Yfloat64}2、初始化方法-指针:rect1:=new(Vertex)rect2:=&Vertex...

Go复合数据类型:结构体

一种通用的、对实体对象进行聚合抽象的能力,在Go中,提供这种聚合抽象能力的类型是结构体类型,也就是struct。自定义一个新类型在Go中,我们自定义一个新类型一般有两种方法。第一种是类型定义...

Go语言基础:方法

导读在阅读本文章前,假定你具备如下能力:?已掌握结构体1.方法1.1方法的概念在理解程序中方法的概念时,我们先看看现实中的一些情况,这样相对比较好理解一些。在农村的朋友可能会知道,在医疗落后的情况...

为什么 Go 语言 struct 要使用 tags

在Go语言中,struct是一种常见的数据类型,它可以用来表示复杂的数据结构。在struct中,我们可以定义多个字段,每个字段可以有不同的类型和名称。除了这些基本信息之外,Go还提供了s...

一文带你掌握掌握 Golang结构体与方法

1.Golang结构体的概念及定义结构体是Golang中一种复合类型,它是由一组具有相同或不同类型的数据字段组成的数据结构。结构体是一种用户自定义类型,它可以被用来封装多个字段,从而实现数据的...

取消回复欢迎 发表评论: