0
点赞
收藏
分享

微信扫一扫

【JavaSE】----- 面向对象编程

独西楼Q 2022-03-31 阅读 74

目录

一、包(package)

🍓导入包中的类

🍓静态导入

🍓将类放到包中

🍓包的访问权限

🍓常见的系统包

二、继承

🍓背景

🍓语法规则

🍓super关键字 

🍓super 与 this 的区别

🍓protected 关键字

🍓更复杂的继承关系

🍓final 关键字

三、组合

四、多态

🍓向上转型

🍓动态绑定

🍓方法重写

🍓向下转型

🍓在构造方法中调用重写的方法(一个坑)

🍓理解多态

🍓使用多态的好处

🍓总结

五、抽象类

🍓语法规则

🍓抽象类内容总结

六、接口

🍓接口内容总结 

🍓对象的比较

🍓深拷贝与浅拷贝代码演示


一、包(package)

  • 是组织类的一种方式.
  • 使用包的主要目的是保证类的唯一性.

🍓导入包中的类

  • Java 中已经提供了很多现成的类供我们使用。

🌊代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        //打印数组
        System.out.println(java.util.Arrays.toString(arr));

        java.util.Date  date = new java.util.Date();
        //得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}
  • 可以使用 java.util.Date 这种方式引入 java.util 这个包中的 Date 类与Arrays 类。

🌊上面这种写法比较麻烦一些, 通常使用 import 语句导入包。代码示例:

import java.util.Arrays;
import java.util.Date;

public class TestDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        //打印数组
        System.out.println(Arrays.toString(arr));

        Date  date = new java.util.Date();
        //得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}

🌊代码示例:

import java.util.*;
public class TestDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        //打印数组
        System.out.println(Arrays.toString(arr));

        Date  date = new java.util.Date();
        //得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}

解决上面的错误需要使用完整的类名:java.util. Date

import java.util.*;
import java.sql.*;

public class TestDemo {
    public static void main(String[] args) {
       java.util. Date  date = new java.util.Date();
        //得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}

🍓静态导入

🍎 使用 import static 可以导入包中的静态的方法和属性

🍓将类放到包中

⭐操作步骤:

(1)在 IDEA 中先新建一个包: 右键 src ➡ 新建 ➡ 包

(2)在弹出的对话框中输入包名, 例如 com.bit.demo1

(3) 在包中创建类, 右键包名 ➡ 新建 ➡ 类, 然后输入类名即可


(4)此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了

(5)在包中新创建的 Test.java 文件的最上方, 出现了一个 package 语句

🍎package 这条语句:指定当前 Test 类在 com.bit.demo1 这个包中。

🍓包的访问权限

  • 前面已经介绍了类中的 public 和 private。 private 中的成员只能被类的内部使用。
  • 包访问权限:只能在当前包中使用,当成员变量不加任何访问修饰限定词时,默认就是包访问权限。此时这个成员可以在包内部的其他类使用, 但是不能在包外部的类使用。

🌊代码示例:

//在 Test 类中创建了一个变量
package com.bit.demo1;

public class Test {
    int val = 10; // 默认是包访问权限
}

//在与 Test 类的同一个包下的 TestDemo 类中创建 Test 对象,并使用 Test 类中的成员变量
package com.bit.demo1;

public class TestDemo {
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.val);
    }
}

运行结果

不同包中的类不可以访问。

🍓常见的系统包

二、继承

🍓背景

🌊代码示例:

class Dog{
    public String name;
    public int age;

    public void eat(){
        System.out.println("eat()");
    }
}

class Bird{
    public String name;
    public int age;
    public String wing;

    public void eat(){
        System.out.println("eat()");
    }

    public void fly(){
        System.out.println("fly()");
    }
}

继承:对共性的抽取,使用 extends 关键字进行处理。意义在于:可以对代码进行重复使用。

🍓语法规则

基本语法

class 子类 extends 父类 {

}

对于上面的代码, 可以使用继承进行改进. 让 Dog 和 Bird 继承自 Animal 类。

🌊代码示例:

class Animal{
    public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "正在吃");
    }
}

class Dog extends Animal{

}

class Bird extends Animal{
    public String wing;

    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小黑";
        dog.eat();
        Bird bird = new Bird();
        bird.name = "大白";
        bird.eat();
        bird.fly();
    }
}

运行结果

 如果把 name 改成 private, 那么此时子类就不能访问了。

🍓super关键字 

🌊代码示例:

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal{
    public Dog(String name,int age){
        //调用父类带有两个参数的构造方法
        super(name,age); //显示调用父类的构造方法
    }
}

🌊代码示例:

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
}

class Bird extends Animal{
    public String wing;

    public Bird(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("小黑",15);
        dog.eat();
        Bird bird = new Bird( "大白",5);
        bird.eat();
        bird.fly();
    }
}

内存布局

运行结果

//Dog 类中默认的构造方法   

 public Dog(){
        super(); 
    }

如果子类中的成员变量与父类的成员变量同名,在调用时没有使用super关键字,就优先访问子类的成员变量。

🌊代码示例:

class Animal{
    public String name;
    public int age;

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

class Bird extends Animal{

    public String wing;
    public String name;

    public Bird(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Bird bird = new Bird( "大白",5);
        System.out.println(bird.name);
        bird.fly();
    }
}

运行结果

🍓super 与 this 的区别

🍓protected 关键字

  • 同一个类中无论使用什么关键字修饰都可以访问,所以就不演示了。
  • default 包访问权限,前面已经演示了。
  • public 修饰的成员变量或方法,在哪里使用都可以,所以就不演示了。

protected 修饰的成员变量或方法 ➡ 被同一个包中的其他类访问

protected 修饰的成员变量或方法 ➡ 被不同包中的子类访问(继承的类也是public修饰的)

什么情况下用哪一种关键字修饰?

🍓更复杂的继承关系

  • 刚才的例子中, 只涉及到 Animal, Dog 和 Bird 三种类. 但是如果情况更复杂一些呢?
  • 例如 Cat 的情况, 可能需要表示更多种类的猫。

如果使用继承方式来表示, 就会涉及到更复杂的体系。

🌊代码示例:

// Animal.java
public Animal {
...
}
// Cat.java
public Cat extends Animal {
...
}
// ChineseGardenCat.java
public ChineseGardenCat extends Cat {
...
}
// OrangeCat.java
public Orange extends ChineseGardenCat {
...
}
......

这样的继承方式称为多层继承, 即子类还可以进一步的再派生出新的子类.

  • 一般不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构。
  • 如果想从语法上进行限制继承, 就可以使用 final 关键字。

🍓final 关键字

💦平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承。

三、组合

  • 和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果.

🌊代码示例:表示一个学校

public class Student {
...
}
public class Teacher {
...
}
public class School {
    public Student[] students;
    public Teacher[] teachers;
}
  • 组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段.

四、多态

🍓向上转型

父类 (Animal) 的引用, 指向一个子类 (Dog) 的对象,这种写法称为 向上转型。

(1)直接赋值

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
}
public class TestDemo {
    public static void main(String[] args) {
      /*  Dog dog = new Dog("小黑",15);
        Animal animal = dog; // 父类引用  引用 子类对象*/

        //向上转型
        Animal animal = new Dog("小黑",15); // 父类引用  引用 子类对象
        animal.eat();
    }
}

 运行结果

(2)方法传参

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
}
public class TestDemo {
    public static void func(Animal animal){
        System.out.println(animal.name+"是"+animal.age+"岁");
    }
    public static void main(String[] args) {
        Dog dog = new Dog("小黑",15);
        func(dog);
    }
}

运行结果

 (3)方法返回

package com.bit.demo3;

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
}
public class TestDemo {
    public static Animal func(){
        Dog dog = new Dog("小黑",19);
        return dog;
    }

    public static void main(String[] args) {
        Animal animal = func();
        System.out.println(animal);
    }
}

运行结果

🍓动态绑定

  • 当子类与父类出现同名的方法时,会调用哪个方法?

🌊代码示例:

class Animal{
    public String name;
    public int age;

    public Animal(String name,int age){
        this.name = name;
        this.age =age;
    }
    //父类的 eat 方法
    public void eat(){
        System.out.println(this.name + "正在吃");
    }
}

class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
    //子类的 eat 方法
    @Override
    public void eat(){
        System.out.println(name + "正在吃骨头");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Animal animal1 = new Animal("大白",20);
        animal1.eat();

        //向上转型
        Animal animal2 = new Dog("小黑",15); // 父类引用  引用 子类对象
        animal2.eat();
    }
}

 运行结果

  • animal1 和 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的对象, animal2 指向 Dog 类型的对象.
  • animal1 和 animal2 分别调用 eat 方法,  animal1.eat() 实际调用了父类的方法, 而 animal2.eat() 调用了子类的方法。

通过父类引用,只能访问父类自己的成员 

🌊代码示例:

class Animal{
    public String name;
    public int age;

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

class Dog extends Animal {
    public String name = "嘿嘿";
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Animal animal = new Animal("大白",10);
        System.out.println(animal.name);
    }
}

运行结果

🍓方法重写

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

✨协变类型(方法重写,返回值不同)

(2)重写的方法, 可以使用 @Override 注解来显式指定 

(3)重写与重载的区别:

🍓向下转型

  • 向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象.
  • 相比于向上转型来说, 向下转型没那么常见,但是也有一定的用途。

🌊代码示例:

class Animal{
    public String name;
    public int age;

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

class Bird extends Animal {

    public String wing;
    public Bird(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }
    public void fly(){
        System.out.println(this.name+"正在飞");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Animal animal = new Bird("小鸟",6);
        //向下转型
        Bird bird = (Bird)animal;  //需要强制类型转换
        bird.fly();
    }

运行结果

向下转型有时是不靠谱的

🌊代码示例:

public class TestDemo {
    public static void main(String[] args) {
        Animal animal = new Bird("小鸟",6);
        //向下转型
        if(animal instanceof Bird) {
            Bird bird = (Bird)animal;  //需要强制类型转换
            bird.fly();
        }
    }
}

🍓在构造方法中调用重写的方法(一个坑)

🌊代码示例:

class Animal{
    public String name = "hello";
    public int age;

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

class Dog extends Animal {
    public Dog(String name,int age){
        super(name,age); //调用父类带有两个参数的构造方法
    }

    @Override
    public void eat(){
        System.out.println(name+"在吃骨头");
    }
}

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

运行结果

🍓理解多态

🌊代码示例:

class Shape{
    public void draw(){

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

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

/***************分割线******************/

public class TestDemo {
    //打印单个图形
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        Rect rect = new Rect();
        drawMap(rect);
        Flower flower = new Flower();
        drawMap(flower);
    }
}

🍓使用多态的好处

(1)类调用者对类的使用成本进一步降低.

(2)可扩展能力更强

  • 如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低
 class Triangle extends Shape{
        @Override
        public void draw() {
            System.out.println("△");
        }
    }

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

例如:需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下

public class TestDemo {
    public static void drawMap(){
        Rect rect = new Rect();
        Flower flower = new Flower();
        Triangle triangle = new Triangle();

        String[] shapes = {"triangle", "rect", "triangle", "rect", "flower"};
        for (String shape : shapes) {
            if (shape.equals("triangle")) {
                triangle.draw();
            } else if (shape.equals("rect")) {
                rect.draw();
            } else if (shape.equals("flower")) {
                flower.draw();
            }
        }
    }
    public static void main(String[] args) {
        drawMap();
    }
}

如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单

public class TestDemo {
    public static void drawMap(){
        Rect rect = new Rect();
        Flower flower = new Flower();
        Triangle triangle = new Triangle();

        Shape[ ] shapes = {triangle, rect, triangle, rect, flower};
        for (Shape shape : shapes) {
            shape.draw();
        }
    }
    public static void main(String[] args) {
        drawMap();
    }
}

🌌什么叫 "圈复杂度" ?

🍓总结

五、抽象类

🍓语法规则

🍓抽象类内容总结

1、什么是抽象方法?

  • 一个没有具体实现的方法,被abstract修饰。

2、包含抽象方法的类叫做抽象类

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

3、抽象类不可以被实例化(new)

4、 抽象类不能被实例化,只能被继承。

5、抽象类中可以包含其他的非抽象方法, 也可以包含成员变量。

  • 非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用

6、一个普通类继承了一个抽象类,那么这个普通类中,需要重写抽象类中所有抽象的方法。

🌊代码示例:

abstract  class Shape{
    int val = 10;
    public abstract void draw();

    public void func(){
        System.out.println("func()方法");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦");

        //调用抽象类中的普通方法
        super.func();
    }
}

class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("❀");

        //调用抽象类中的成员变量
        System.out.println(val);
    }
}

public class Test {
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        //向上转型
        Shape shape = new Rect() ;
        drawMap(shape);

        Flower flower = new Flower();
        drawMap(flower);
    }
}

运行结果

7、一个抽象类 B 如果继承了一个抽象类 A,那么抽象 B 可以不实现抽象父类 A 的抽象方法。

🌊代码示例: 

abstract class A{
    public abstract void funcA();
}
abstract class B extends A{
    public abstract void funcB();
}

8、结合第8点,当抽象类 B 再次被一个普通类 C 继承后,那么普通类 C 中必须重写 A 和 B 这两个抽象类当中的抽象方法。

🌊代码示例:

abstract class A{
    public abstract void funcA();
}

abstract class B extends A{
    public abstract void funcB();
}

class C extends B{
    @Override
    public void funcA() {
        
    }
    @Override
    public void funcB() {
        
    }
}

9、抽象类不可以被 final 修饰。

10、抽象方法不可以被 final 和 private 修饰。

11、抽象类的作用:

六、接口

语法规则

  • 在刚才的代码中, 我父类 Shape 并没有包含别的非抽象方法, 也可以设计成一个接口

🍓接口内容总结 

1、接口使用 interface 修饰的。 interface IA {}

interface IShape{
    public abstract void draw();
}

2、接口当中的普通方法不能具体的实现,如果非要实现,只能通过关键字 default 修饰这个方法。

interface IShape{
    public abstract void draw();
    
    default public void func(){
        System.out.println("default 修饰的func()方法");
    }
}

3、接口中可以有 static 修饰的方法

interface IShape{
    public abstract void draw();

    public static void func(){
        System.out.println("static 修饰的func()方法");
    }
}

4、接口中的方法一定是 public, 因此可以省略 public

5、接口中的抽象方法默认是 public abstract 的, 因此可以省略 public abstract

interface IShape{
    //抽象方法
    void draw();

     static void func(){
        System.out.println("static 修饰的func()方法");
    }
}

6、接口不可以被实例化(new)。

7、类和接口之间的关系是通过 implements 实现的。

8、当一个类实现了一个接口,就必须重写接口当中的抽象方法。

9、当一个类实现一个接口之后,重写这个方法的时候,方法前面必须加上public。

10、接口在调用的时候可以创建一个接口的引用, 对应到一个子类的实例。

🌊代码示例:

interface IShape{
    public abstract void draw();

    default public  void func(){
        System.out.println("default 修饰的func()方法");
    }
}

class Rect implements IShape {
    @Override
    public void draw() {
        System.out.println("♦");
    }

    @Override
    public void func() {
        System.out.println("重写接口中的func()方法");
    }
}

class Flower implements IShape {
    @Override
    public void draw() {
        System.out.println("❀");
    }
}
public class Test {
    public static void drawMap(IShape iShape){
        iShape.draw();
    }
    public static void main(String[] args) {
        //向上转型
        IShape iShape = new Rect();
        drawMap(iShape);
        //调用子类 Rect 中重写的func方法
        iShape.func();
        
        Flower flower = new Flower();
        drawMap(flower);
    }
}

运行结果

 11、接口中的成员变量默认是public static final 修饰的

interface IShape{
    int a = 10; //因为默认是 public static final 修饰的(常量)必须要初始化
    
    void draw(); //抽象方法
}

12、 一个类可以通过关键字 extends 继承一个抽象类或者普通类,但是只能继承一个类。同时,可以通过 implements 实现对多个接口,接口之间使用逗号隔开。

🌊代码示例:

interface IA{
    int A = 10;
    void funcA();
}
interface IB{
    void funcB();
}
abstract class BClass{
    public abstract void funcBClass();
}
class AClass extends BClass implements IA,IB{
    //重写接口IA中的 funcA() 方法
    @Override
    public void funcA() {
        System.out.println("重写接口中的funcA()方法");

        System.out.println("调用接口中的成员变量A:"+A);
    }
    //重写接口IB中的 funcB() 方法
    @Override
    public void funcB() {
        System.out.println("重写接口中的funcB()方法");
    }
    //重写抽象类BClass中的 funcBClass() 方法
    @Override
    public void funcBClass(){
        System.out.println("重写抽象类中的funcBClass()方法");
    }
}

13、接口和接口之间可以使用 extends 来操作他们的关系,此时 extends 意为:拓展。

🌊代码示例: 

interface IA{
    void funcA();
}
interface IB extends IA{
    void funcB();
}
class C implements IB{
    @Override
    public void funcB() {

    }

    @Override
    public void funcA() {

    }
}

14、实现多个接口

🌊代码示例: 

class Animal{
    public String name;

    public Animal(String name) {
        this.name = name;
    }
}
//不是所有的动物都会飞,所以不能写到Animal类中。
//写到另一个类中也不行,因为一个类不能继承多个类,所以使用接口实现。
interface IFlying{
    void fly();
}
//跑的接口
interface IRunning{
    void run();
}
//游泳的接口
interface ISwimming{
    void swim();
}
//鸟会飞
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 swim() {
        System.out.println(this.name+"正在游泳");
    }
}
//鸭子会跑、会飞、会游泳
class Duck extends Animal implements IRunning,IFlying,ISwimming{
    public Duck(String name){
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name+"正在跑");
    }

    @Override
    public void fly() {
        System.out.println(this.name+"正在飞");
    }

    @Override
    public void swim() {
        System.out.println(this.name+"正在游泳");
    }
}
public class Test1 {
    //调用飞的方法
    public static void Flyingfunc(IFlying iFlying){
        iFlying.fly();
    }
    //调用跑的方法
    public static void Runningfunc(IRunning iRunning){
        iRunning.run();
    }
    //调用游泳的方法
    public static void Swimmingfunc(ISwimming iSwimming){
        iSwimming.swim();
    }
    public static void main(String[] args) {
        /*Bird bird = new Bird("小鸟");
        Flyingfunc(bird);*/
        Flyingfunc(new Bird("小鸟"));

        Flyingfunc(new Duck("鸭子"));
        Runningfunc(new Duck("鸭子"));
        Swimmingfunc(new Duck("鸭子"));

        Runningfunc(new Frog("青蛙"));
        Swimmingfunc(new Duck("青蛙"));
    }
}

运行结果

15、三个常用接口

🎄Comparable 接口

🌊代码示例: 给定一个学生类

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

🌊代码示例: 再给定一个学生对象数组

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",20,89.6);
        students[1] = new Student("lisi",22,77.6);
        students[2] = new Student("wangwu",19,66.6);
        System.out.println(Arrays.toString(students));
    }
}

对这个对象数组中的元素进行排序。

数组有一个现成的 sort 方法, 能否直接使用这个方法呢?

🌊代码示例:  

//使用comparable接口,比较学生类
class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

    //额外指定数组比较的内容
    @Override
    public int compareTo(Student o) {
        //按年龄比较
        //谁调用compareTo方法,谁就是this

       /* if(this.age>o.age){
            return 1;
        }else if(this.age<o.age){
            return -1;
        }else {
            return 0;
        }*/

        //代码简化
        return this.age - o.age;     //从小到大排序

       // return o.age - this.age;  //从大到小排序
    }
}    

//按分数比较
//成绩是double类型的,所以要进行强制类型转换
return (int)(this.score - o.score);

//按姓名比较
//要是用 compareTo() 方法进行比较
return this.name.compareTo(o.name);

🎄Comparator 接口

🌊代码示例: 给定一个学生类

class Student {
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

🌊代码示例: 再给定一个学生对象数组

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",20,89.6);
        students[1] = new Student("lisi",22,77.6);
        students[2] = new Student("wangwu",19,66.6);
        System.out.println(Arrays.toString(students));
    }
}

🌊代码示例: 写一个 AgeComparator 类实现 Comparator 接口。按年龄进行比较

//按年龄进行比较
class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}

🌊代码示例: 对 AgeComparator 类进行实例化,在sort方法中调用 AgeComparator 对象,对数组进行比较。

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",20,89.6);
        students[1] = new Student("lisi",22,77.6);
        students[2] = new Student("wangwu",19,66.6);
        System.out.println("比较前:");
        System.out.println(Arrays.toString(students));

        //实例化 AgeComparator
        AgeComparator ageComparator = new AgeComparator();

        System.out.println("比较后:");

        //sort方法中,传入学生数组,传入对学生数组进行比较的对象的引用(按年龄比较)
        Arrays.sort(students,ageComparator);

        System.out.println(Arrays.toString(students));
    }
}

运行结果

🌊代码示例:  

//按分数进行比较
class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return (int)(o1.score - o2.score);
    }
}

ScoreComparator scoreComparator = new ScoreComparator();
Arrays.sort(students,scoreComparator);


//按姓名进行比较
class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

NameComparator nameComparator = new NameComparator();
Arrays.sort(students,nameComparator);

🎄Clonable 接口

💦clone()方法:

🌊代码示例:   

class Person implements Cloneable{
    public int age;
    public void eat(){
        System.out.println("吃!");
    }

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

    //重写 clone() 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class TestDemo1  {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        person1.age = 99;
        //clone() 默认是 Object 类,所以将其进行强制类型转换
        Person person2 = (Person) person1.clone();
        System.out.println(person2.age);
        System.out.println("====================");
        person2.age=50;
        System.out.println(person1.age);
        System.out.println(person2.age);
    }

}

内存布局

运行结果

一个类生成的对象要想被克隆,必须要实现Cloneable接口。实现Cloneable接口后,要重写 clone()方法,同时要抛出异常。

🍓对象的比较

🌊代码示例:   

class Card {
    public int rank; // 数值
    public String suit; // 花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public boolean equals(Object o) {
        // 如果this引用与o引用指向的是同一个对象,就返回true
        if (this == o) {
            return true;
        }
        // o如果是null对象,或者o不是Card的子类
        if (o == null || !(o instanceof Card)) {
            return false;
        }
        // 注意基本类型可以直接比较,但引用类型最好调用其equal方法
        Card c = (Card) o;
        return rank == c.rank && suit.equals(c.suit);
    }
}

public class TestDemo1{
    public static void main(String[] args) {
        Card card1 = new Card(2, "♠");
        Card card2 = new Card(2, "♠");
        System.out.println(card1.equals(card2));
    }
}

运行结果:true

🌌三种方式对比:

🍓深拷贝与浅拷贝代码演示

  • 决定是深拷贝还是浅拷贝不是方法的用途,是代码的实现。

🌊浅拷贝代码示例:

class Money{
    public double m = 66.66;
}
class Person implements Cloneable{
    public int age;
    Money money = new Money();

    public void eat(){
        System.out.println("吃!");
    }

    @Override
    public String toString() {
        return "demo1.Person{" +
                "age=" + age +
                '}';
    }

    //重写 clone() 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class TestDemo1  {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person) person1.clone();
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);
        System.out.println("================");
        person2.money.m = 33.33;
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);
    }
}

内存布局

运行结果

  • 只克隆了Person对象,并没有将Person对象中的Money对象克隆,所以是浅拷贝。

🌊将上面的代码变成深拷贝:

class Money implements Cloneable{
    public double m = 66.66;

    //重写 clone() 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable{
    public int age;
    Money money = new Money();

    public void eat(){
        System.out.println("吃!");
    }

    @Override
    public String toString() {
        return "demo1.Person{" +
                "age=" + age +
                '}';
    }

    //重写 clone() 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        tmp.money = (Money) this.money.clone();
        return tmp;
    }
}

public class TestDemo1  {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person) person1.clone();
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);
        System.out.println("================");
        person2.money.m = 33.33;
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);
    }
}

内存布局

 运行结果

 16、抽象类和接口的区别

举报

相关推荐

0 条评论