目录
- [1. 抽象类](#1. 抽象类)
-
-
- [1.1 抽象类语法](#1.1 抽象类语法)
- [1.2 抽象类特性](#1.2 抽象类特性)
- [1.3 抽象类的作用](#1.3 抽象类的作用)
-
- [2. 接口](#2. 接口)
-
-
- [2.1 接口语法](#2.1 接口语法)
- [2.2 接口的特性](#2.2 接口的特性)
-
- [3. 接口案例](#3. 接口案例)
- [4. 常用接口](#4. 常用接口)
-
-
- [4.1 Comparable接口---compareTo()方法](#4.1 Comparable接口---compareTo()方法)
- [4.2 clonable接口---clone方法](#4.2 clonable接口---clone方法)
- [4.2 深拷贝和浅拷贝](#4.2 深拷贝和浅拷贝)
-
- [5. Object类](#5. Object类)
-
-
- [5.1 equals()方法](#5.1 equals()方法)
- [5.2 toString()方法](#5.2 toString()方法)
- [5.3 hashCode()方法](#5.3 hashCode()方法)
-
- [6. 内部类](#6. 内部类)
-
-
- [6.1 内部类分类](#6.1 内部类分类)
-
1. 抽象类
并不是所有的类都用来描述对象,如果一个类中没有足够的信息来描述一个具体的对象,那这个类就是抽象类。一个方法没有方法体,这就是抽象方法。
1.1 抽象类语法
被abstract修饰的类称为抽象类,被abstract修饰的方法称为抽象方法,abstract不修饰成员变量。
【代码演示】
java
abstract class Animal{
//普通方法
public void fun(){
}
//抽象方法 有抽象方法后,当前类必须也变成抽象类
public abstract void fun1();
}
1.2 抽象类特性
- 抽象类中可以包含普通方法和成员
- 抽象方法不能被private,final和static修饰(因为抽象方法生来就是要被继承并重写的,被private修饰虽能被继承,但不能被重写。重写的条件就是使用抽象类的条件)
- 抽象类不能实例化,必需由子类继承并重写抽象类的全部抽象方法。若继承的子类也是抽象类,该子类也不能实例化,想使用,必须让普通类继承抽象类
- 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
- 抽象类可以提供构造方法,供子类创建对象时,初始化父类的成员变量
1.3 抽象类的作用
普通类也可以做到抽象类能做到的,而且抽象类使用起来不方便,为什么要有抽象类?
答:和final一样,是为了充分利用编译器的校验功能。让编译器来检查,以防不留神就作出更改。
2. 接口
接口,属于引用数据类型,是一种公共的行为规范。在实现时只要符合规范标准,就可以用。在java中,接口可以看做多个类的公共规范,比抽象类更抽象
2.1 接口语法
定义接口跟定义一个类基本相同
java
修饰符 interface I形容词{
//抽象方法
void draw(); //等价于public abstract void draw();
}
【定义规则】
- 接口的命名以I开头,词性为形容词
- 根据阿里代码规范,接口中的方法和属性不要加任何修饰(因为加不加都一样),保持代码简洁
2.2 接口的特性
- 接口不能直接实例化
- 接口也有字节码文件
- 使用接口需要重写接口中的所有抽象方法
- 接口中方法必须是抽象方法,但方法可以被static和default修饰,这两个方法都不是抽象方法,需要实现方法细节
- 接口之间继承用extends,实现接口用implements。一个类若是同时用extends和implements,先extends后implements
- 接口中不能有静态/实例代码块和构造方法
- 接口解决了多继承的问题。因为把所有的方法都放到一个类会导致该类没有功能的专一性,但java有不支持继承多个类,但接口就可以。
- 接口可以被abstract修饰
3. 接口案例
【案例一】使用单接口写接口和电脑的功能
java
/**
* 1. USB接口:包含打开设备、关闭设备功能
* 2. 笔记本类:包含开机功能、关机功能、使用USB设备功能
* 3. 鼠标类:实现USB接口,并具备点击功能
* 4. 键盘类:实现USB接口,并具备输入功能
*/
interface USB{
void openDevice();
void closeDevice();
}
class Mouse implements USB{
public void click(){
System.out.println("鼠标点击");
}
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
}
class keyBoard implements USB{
public void input(){
System.out.println("键盘输入");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
@Override
public void openDevice() {
System.out.println("打开键盘");
}
}
class Computer implements USB{
public void powerOn(){
System.out.println("打开电脑");
}
public void powerOff(){
System.out.println("关闭电脑");
}
public void useDevice(USB usb){
if(usb instanceof Mouse){
Mouse mouse = (Mouse)usb;
mouse.click();
} else if (usb instanceof keyBoard) {
keyBoard keyBoard = (keyBoard)usb;
keyBoard.input();
}
}
@Override
public void openDevice() {
System.out.println("电脑开机");
}
@Override
public void closeDevice() {
System.out.println("电脑关机");
}
}
public class MyInterface {
public static void fun(USB usb){
usb.closeDevice();
}
public static void main(String[] args) {
fun(new keyBoard());
}
}
【案例二】使用实现多接口写动物能跑 能游泳
java
class Animal1 {
public int age;
}
interface run{
void run();
}
interface swim{
void swim();
}
class Dog extends Animal1 implements run,swim{
@Override
public void run() {
System.out.println("狗狗跑步");
}
@Override
public void swim() {
System.out.println("狗狗游泳");
}
}
public class MyInterface {
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
}
}
4. 常用接口
4.1 Comparable接口---compareTo()方法
sort方法中调用了compareTo()方法
【代码演示】
java
class Student{
public int age;
public Student(int age){
this.age = age;
}
}
public class Test {
/**
* 之前整型数组调用sort方法,进行排序
* 现在要求:把几个学生对象排序
*/
public static void main(String[] args) {
Student[] students = {
new Student(20),
new Student(18),
new Student(25),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
//运行结果
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
【代码改正】
java
class Student implements Comparable{
public int age;
//重写compareTo方法,指定 到底比较引用类型的哪个变量
@Override
public int compareTo(Object o) {
Student student = (Student) o;
if (this.age > student.age)
return 1;
else if (this.age == student.age)
return 0;
else
return -1;
}
//重写toString方法,改变打印效果
@Override
public String toString() {
return "Student{" +
"age=" + age +
'}';
}
public Student(int age){
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Student[] students = {
new Student(20),
new Student(18),
new Student(25),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
//运行结果
[Student{age=18}, Student{age=20}, Student{age=25}]
4.2 clonable接口---clone方法
拷贝的是对象,不是类
Object类是所有类的子类,且有clone方法,为什么自己写的类不能调用?为什么要重写?
- 因为clone方法是protected修饰的,在不同包的子类中访问需要用super关键字,所以调用不了。
- 想调用clone方法,官方规定必须重写。
- 而且自己写的类必须实现Cloneable接口(空接口/标志接口),标志着该类有比较功能。
【代码演示】
java
/**
* 克隆一个对象:
* 1. 先实现Cloneable接口
* 2. 重写Object类的clone方法
* 3. 由于clone方法是protected方法,要用super关键字访问Object类的clone方法
*
*/
class Animal implements Cloneable{
public int age;
public String name;
//1.可以让编译器自动生成,但需要将父类强转为当前对象
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
//2. 也可以自己重写clone方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Animal(int age, String name){
this.age = age;
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Animal animal = new Animal(20,"西西");
Animal animal1 = (Animal) animal.clone();
System.out.println(
"animal.age="+animal.age
+" animal1.age="+animal1.age
);
}
}
//运行结果
animal.age=20 animal1.age=20
4.2 深拷贝和浅拷贝
拷贝分深拷贝和浅拷贝,辨别深浅拷贝在于代码实现方式,而不是方法。
java
演示浅拷贝
class Person{
int num = 7;
}
class Animal implements Cloneable{
public int age;
public String name;
public Person person = new Person();
//1.可以让编译器自动生成,但需要将父类强转为当前对象
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
//2. 也可以自己重写clone方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Animal(int age, String name){
this.age = age;
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Animal animal = new Animal(20,"西西");
Animal animal1 = (Animal) animal.clone();
animal.person.num = 10;
System.out.println(
"animal.person.num="+animal.person.num
+" animal.person.num="+animal1.person.num
);
}
}
//运行结果,我只改了animal对象的值,
//两个对象的值却都变了
animal.person.num=10 animal.person.num=10
5. Object类
Object类需要掌握下面这些方法,可以根据图标🔒,🔓来判断该方法是否能被重写,有🔒标志的就是被private修饰的,不能重写
5.1 equals()方法
简单数据类型比较相等用==,引用数据类型比较相等用equals方法
java
/**
* equals方法 比较引用类型
* 和 == 不同
*/
class P{
public int age;
public P(){
}
public P(int age){
this.age = age;
}
//查看equals方法的源代码发现和person==person1一样的效果
//重写equals方法自己定义
@Override
public boolean equals(Object obj) {
P p = (P)obj;
if (this.age == p.age)
return true;
else
return false;
}
}
public class Test1 {
public static void main(String[] args) {
P person = new P(18);
P person1 = new P(18);
System.out.println(person==person1); //false
System.out.println(person.equals(person1));//true
}
}
5.2 toString()方法
上面已演示
5.3 hashCode()方法
主要是获取"地址",先不讲,后期会仔细讲
6. 内部类
外部类:类A中定义了一个类B,类A就是外部类
内部类:类B就是内部类
6.1 内部类分类
1.实例内部类
- 非静态类,无论是内部类还是外部类,其内部不能有static。若想定义静态的必须加final,而且该变量必须就地初始化。
- 如何实例化 实例内部类?先创建外部类对象。外部类.内部类 变量名 = 外部类对象引用.内部类();
- 在内部类中访问外部类成员,不论访问权限。类内部和类外部同名变量时,优先看内部类。若非要访问外部类的同名变量,用外部类类名.成员变量。
- 在外部类访问内部类,实例化内部类再调用即可
- 内部类也有字节码文件,命名格式为外部类类名$内部类类名.class
【代码演示】
java
class Out{
public int a;
public static int b; //报错
public static final int b;
private int c;
//静态内部类
class InnerClass{
public int a = 2;
public int d;
public static final int e = 2;
private int f;
public void test(){
//在内部类中访问外部类成员
System.out.println(a); //优先访问内部类 2
System.out.println(b); //访问外部类 0
Out out = new Out();
System.out.println(out.c);//访问外部类 0
System.out.println(d);//优先访问内部类 0
System.out.println(e);//优先访问内部类 2
System.out.println(f);//优先访问内部类 0
System.out.println("InnerClass");
}
}
public void test(){
//在外部类中访问内部类成员
InnerClass innerClass = new InnerClass();
System.out.println(innerClass.d);
System.out.println(InnerClass.e);
System.out.println(innerClass.f);
System.out.println(a);
System.out.println("Out");
}
}
public class test {
public static void main(String[] args) {
//创建 实例内部类对象
//1.创建父类对象
OutClass outClass = new OutClass();
//2.通过 外部类对象 引用创建 内部类对象 外部类.内部类 变量名 = 外部类对象引用.内部类();
OutClass.InnerClass innerClass = outClass.new InnerClass();
innerClass.test();
}
}
2.静态内部类(工作中用的较多)
- 如何实例化?不需要创建外部类对象。外部类.内部类
- 在内部类中访问只能访问外部类的静态成员,若要访问非静态成员,需要创建一个外部类对象,通过对象引用来调用
【代码演示】
java
class Out{
public int a;
public static int b;
private int c;
//静态内部类
static class InnerClass{
public int a = 2;
public int d;
public static final int e = 2;
private int f;
public void test(){
System.out.println(a);
System.out.println(b);
Out out = new Out();
System.out.println(out.c);
System.out.println(d);
System.out.println(e);
System.out.println(f);
System.out.println("InnerClass");
}
}
}
public class test {
public static void main(String[] args) {
//创建 静态内部类对象
Out.InnerClass innerClass1 = new Out.InnerClass();
innerClass1.test();
}
}
3.匿名内部类(多线程常用)
【代码演示】
java
interface A{
void func();
}
public class test {
public static void main(String[] args) {
//创建 匿名内部类对象
A a = new A(){
@Override
public void func() {
}
};
}
}
4.局部内部类(不常用,了解即可)
- 只能定义在外部类方法内部,只能在方法体内部使用。
- 不能被public,static等访问修饰限定符修饰
- 有自己的字节码文件,格式为外部类类名$数字内部类类名.class
【代码演示】
java
public class test {
public static void fun(){
class Inner{
public int a = 89;
}
Inner inner = new Inner();
System.out.println(inner.a);
}
public static void main(String[] args) {
fun();
}