目录
一、包(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、抽象类和接口的区别