1、抽象类与抽象方法(abstract关键字)
1.1、在Java中,抽象类是一种不能实例化的类,它被用作其他类的基类。抽象类通过关键字abstract
进行声明。抽象类可以包含抽象方法和非抽象方法。
抽象类: 被abstract修饰的类
public abstract class Animal{}
抽象方法:被abstract修饰的方法:在父类的方法中,没有具体的函数实现,只给出了声明,其中包含抽象方法的必须是一个抽象类
public abstract void eat();
如下代码所示:
Animal类(抽象类)
public abstract class Animal {
private String name;
private int age;
//这里定义了抽象方法,类就必须写成抽象类
public abstract void eat();
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Animal{name = " + name + ", age = " + age + "}";
}
}
Dog类:
public class Dog extends Animal{
private double high;
public Dog() {
super();
}
public Dog(String name,int age,double high) {
super(name,age);
this.high = high;
}
@Override
public void eat() {
System.out.println("狗吃骨头!");
}
/**
* 获取
* @return high
*/
public double getHigh() {
return high;
}
/**
* 设置
* @param high
*/
public void setHigh(double high) {
this.high = high;
}
public String toString() {
return "Dog{name = " + super.getName() + "age = " + super.getAge() + "high = " + this.getHigh() + " }" ;
}
}
Cat类:
public class Cat extends Animal {
private double high;
public Cat() {
super();
}
public Cat(String name,int age,double high) {
super(name,age);
this.high = high;
}
@Override
public void eat() {
System.out.println("猫吃老鼠!");
}
/**
* 获取
* @return high
*/
public double getHigh() {
return high;
}
/**
* 设置
* @param high
*/
public void setHigh(double high) {
this.high = high;
}
public String toString() {
return "Cat{name = " + super.getName() + "age = " + super.getAge() + "high = " + this.getHigh() + " }" ;
}
}
测试类:
public class MyTest {
public static void main(String[] args) {
Animal an1 = new Cat();
Animal an2 = new Dog();
//调用各自的方法
an1.eat();
an2.eat();
}
}
输出结果:
猫吃老鼠! 狗吃骨头!
结论:子类必须重写抽象类中所有的抽象方法,不然得话子类要是一个抽象类。对于非抽象方法,可以在子类或者通过子类创建的实例对象中调用。抽象类中可以没有抽象方法,但是有抽象方法一定要是抽象类!!!
==注意:不能用abstract修饰私有方法,静态方法,final的方法,final的类!!!==
2、接口(interface)
2.1、接口就是规范,定义的时一组规则,体现了现实社会中“如果你是/要 ….则必须能….”的思想,继承是一个”是不是“的is-a关系,二接口实现规则是”能不能“的has-a关系。只要实现了接口的规范,就会有接口里面提供的方法。
2.2、接口里面可以声明的结构:
①、属性:必须使用public static final修饰。
②、方法:在jdk8之前,声明抽象方法,修饰为public abstract jdk8:声明静态方法,默认方法 jdk9:声明私有方法。
③、不可以声明:构造器、代码块等等。
④、接口不可以实例出具体的对象,但是可以创建接口类型的数组。但是在这个数组中要存放具体实现类的对象。
2.3、接口的简单使用如下所示:
定义飞行的接口:
public interface Fly {
public static final int MIN_SPEED = 0;
//public static final可以省略不写
int MAX_SPEED = 0;
//抽象方法
public abstract void fly();
//public abstract也可以省略不写
void attack();
}
类实现上面的接口:
//如果是实现了多个接口,就打个逗号写在后面
public class Plane implements Fly{
@Override
public void fly() {
System.out.println("飞机在飞!");
}
@Override
public void attack() {
System.out.println("飞机在攻击目标!");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Plane plane = new Plane();
plane.attack();
plane.fly();
}
}
输出结果:
飞机在攻击目标! 飞机在飞!
2.4、如果是又有继承又有接口的情况:
格式: 访问权限 class 类名 extends 父类 implements B,C...{}
从上面可以看出,接口在一定程度上弥补了类单继承的局限性。类必须实现接口中的所有抽象方法!!!
2.5、接口与接口之间的关系:继承,并且可以多继承
public interface 接口名 extends 接口1,接口2{ }
2.6、接口也具有多态性,如下所示:
public class Test {
public static void main(String[] args) {
//测试接口的多态
Fly fly = new Bird();
fly.fly();
fly.attack();
fly = new Plane();
fly.fly();
fly.attack();
}
}
输出结果:
鸟在飞行! 鸟在捕食! 飞机在飞! 飞机在攻击目标!
接口的应用场景,例如电脑的USB接口,代码如下所示:
定义USB的接口
public interface USB {
void work();
void stop();
}
打印机实现这些接口:
//打印机实现USB的接口
public class Printer implements USB {
@Override
public void work() {
System.out.println("打印机开始工作了!");
}
@Override
public void stop() {
System.out.println("打印机停止工作了!");
}
}
电脑已经对进口进行了功能的封装:
public class Computer {
public void ataack(USB usb){
System.out.println("设备连接成功!");
usb.work();
usb.stop();
}
}
测试类:
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.ataack(new Printer());
}
}
输出结果:
设备连接成功! 打印机开始工作了! 打印机停止工作了!
还有一种创建对象的方式,如下所示:
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.ataack(new Printer());
//还可以通过下面这种方式创建临时对象
USB usb = new USB() {
@Override
public void work() {
//具体的重写内容
System.out.println("111开始工作了!");
}
@Override
public void stop() {
//具体的重写内容
System.out.println("111停止工作了!");
}
};
computer.ataack(usb);
}
}
输出结果:
设备连接成功! 打印机开始工作了! 打印机停止工作了! 设备连接成功! 111开始工作了! 111停止工作了!
简写成:
computer.ataack(new USB(){
@Override
public void work(){
}
@Override
public void stop(){
}
});
3、接口新特性:jdk8:声明静态方法,默认方法 jdk9:声明私有方法
如下所示:
//接口
public interface Compare1 {
//定义静态方法
public static void show1(){
System.out.println("这是接口中的静态方法!");
}
//默认方法
public default void show2(){
System.out.println("我是默认方法2!");
}
public default void show3(){
System.out.println("我是默认方法3!");
}
}
//实现类
public class Pare1 implements Compare1 {
@Override
public void show3() {
System.out.println("实现类重写了默认方法3!");
}
}
//测试类
public class Test {
public static void main(String[] args) {
//通过接口直接调用静态方法,接口中的静态方法只能被接口调用,不能用实现类调用!!!继承可以
Compare1.show1();
//创建对象
Pare1 pare1 = new Pare1();
//调用默认方法
pare1.show2();
pare1.show3();
}
}
输出结果:
这是接口中的静态方法! 我是默认方法2! 实现类重写了默认方法3!
3.1、接口异常:类实现了两个接口,并且接口中有两个完全一样的方法,并且在实现类中没有对该方法进行重写。要重写之后才不会报错,虽然他们俩是完全一样的函数,但是只要重写了就相当于重写了他们的任何一个,从而在进行调用的时候才可以进行正确的调用。
3.2、类优先原则:子类继承父类并且实现了接口,父类中有和接口同名同形式的方法,其中接口中的方法为默认方法,那么通过子类调用这个同名方法的时候优先调用的是父类的方法。如果进行重写,那么调用的就是重写后的方法。如果要调用接口里面的方法,可以用以下方式进行调用:
接口名.super.方法名();
4、内部类
4.1、定义:定义在一个类中的类称为内部类。
分类:
成员内部类 (又分为静态内部类和非静态内部类)
局部内部类(同理)
4.2、如何创建成员内部类的实例?
4.3、如何在成员内部类中调用外部类的结构?
代码如下所示:
Person.java
public class Person {
private int age;
private String name = "Tom";
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person{age = " + age + ", name = " + name + "}";
}
//非静态成员内部类
public class Dog1{
public void work(){
System.out.println("Dog1在看家护院!");
}
//内部类调用外部类的结构
public void show(){
System.out.println("姓名为;" + Person.this.name); //调用外部结构的属性
Person.this.print(); //调用外部结构的方法
}
}
//静态成员内部类
public static class Dog2{
public void work(){
System.out.println("Dog2在看家护院!");
}
}
public void show(){
System.out.println("姓名:" + this.name + "\t年龄:" + this.age);
}
public void print(){
System.out.println("HHHHHHH");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建内部类的实例
//非静态内部类
Person person = new Person();
Person.Dog1 dog1 = person.new Dog1();
dog1.work();
dog1.show();
//静态内部类的实例
Person.Dog2 dog2 = new Person.Dog2();
//调用方法
dog2.work();
}
}
输出结果:
Dog1在看家护院! 姓名为;Tom HHHHHHH Dog2在看家护院!
4.4、局部内部类的基本使用。
如下所示:
public class MyComparable {
//我们现在需要返回一个接口的实现类对象
public Comparable returnCompared(){
//创建局部内部类,实现comparable接口
class InComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
//返回实现类的对象
return new InComparable();
}
}
也可以写成匿名对象:
public class MyComparable {
//我们现在需要返回一个接口的实现类对象
public Comparable returnCompared(){
//返回实现类的匿名
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
5、枚举
5.1、枚举本质上也是一种类,只不过是这个类的对象是有限的,固定的几个,不能让用户随意创建。
5.2、开发中的建议:如果针对于某个类,其实实例是确定个数的,那么推荐将此类声明为枚举类。如果枚举类的实例只有一个,那么可以考虑使用单例设计模式。
简单的使用如下所示:
季节的枚举:
public enum Season {
//本质上是public static final Season SPRING = new Season("春天", "春暖花开");
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "寒风刺骨");
private final String seasonName; //季节名称
private final String seasonDesc; //季节的描述
//限制实例对象的个数,那么就需要私有化构造器
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//让对象可以访问内部的属性
public String getSeasonDesc() {
return seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
}
测试类:
public class Test {
public static void main(String[] args) {
System.out.println(Season.AUTUMN.getSeasonDesc());
System.out.println(Season.SPRING.getSeasonDesc());
}
}
输出结果:
秋高气爽 春暖花开
5.3、枚举类有默认的父类,所以不支持继承其它的父类。
5.4、枚举中常用的方法:
①、toString(使用的时候要进行重写!!)
②、name()(返回对象名)
③、values(获取所有的枚举类对象,并且返回一个数组)
④、valueof(通过传入的字符串在枚举中查找,然后返回查找结果,如果不存在,会报错!!)
⑤、ordinal(返回当前枚举常量的序号)
代码如下所示:
public enum Season {
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "寒风刺骨");
private final String seasonName; //季节名称
private final String seasonDesc; //季节的描述
//限制实例对象的个数,那么就需要私有化构造器
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//让对象可以访问内部的属性
public String getSeasonDesc() {
return seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
//测试类
public class Test {
public static void main(String[] args) {
System.out.println(Season.AUTUMN.getSeasonDesc());
System.out.println(Season.SPRING.getSeasonDesc());
//toString方法
System.out.println(Season.WINTER);
//name方法
System.out.println(Season.WINTER.name());
//values方法
Season[] seasons = Season.values();
for (int i = 0; i < seasons.length; i++) {
System.out.println(seasons[i]);
}
//valueof
String season1 = "WINTER";
System.out.println(Season.valueOf(season1));
//ordinal
System.out.println(Season.WINTER.ordinal());
}
}
输出结果:
秋高气爽 春暖花开 Season{seasonName='冬天', seasonDesc='寒风刺骨'} WINTER Season{seasonName='春天', seasonDesc='春暖花开'} Season{seasonName='夏天', seasonDesc='夏日炎炎'} Season{seasonName='秋天', seasonDesc='秋高气爽'} Season{seasonName='冬天', seasonDesc='寒风刺骨'} Season{seasonName='冬天', seasonDesc='寒风刺骨'} 3
5.5、枚举类实现接口的操作
情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法是,执行的是同一个方法。
情况2:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法是,执行的是不同的方法。这个时候直接将方法重写在不同的枚举类对象中。如下所示:
//枚举类
public enum Season implements Show {
//下面这种重写方式,当枚举类对象调用接口中的方法时,调用的就是不同的方法
SPRING("春天", "春暖花开") {
@Override
public void show2() {
System.out.println("枚举类对象中实现接口中的方法!1");
}
},
SUMMER("夏天", "夏日炎炎") {
@Override
public void show2() {
System.out.println("枚举类对象中实现接口中的方法!2");
}
},
AUTUMN("秋天", "秋高气爽") {
@Override
public void show2() {
System.out.println("枚举类对象中实现接口中的方法!3");
}
},
WINTER("冬天", "寒风刺骨") {
@Override
public void show2() {
System.out.println("枚举类对象中实现接口中的方法!4");
}
};
private final String seasonName; //季节名称
private final String seasonDesc; //季节的描述
//限制实例对象的个数,那么就需要私有化构造器
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//让对象可以访问内部的属性
public String getSeasonDesc() {
return seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
//这种实现方式,如果通过不同的枚举类对象调用此方法,那么调用的是同一个方法,即只会调用一次
@Override
public void show1() {
System.out.println("枚举类中实现接口中的方法!");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Season[] seasons = Season.values();
for (int i = 0; i < seasons.length; i++) {
seasons[i].show1();
seasons[i].show2();
}
}
}
输出结果:
枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!1 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!2 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!3 枚举类中实现接口中的方法! 枚举类对象中实现接口中的方法!4
通过枚举实现单例模式:
//用枚举实现单例模式
public enum Person {
//当枚举中只有一个对象时,相当于一种单例模式
PERSON("酋长",21);
private final String name;
private final int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + "}";
}
public void show(){
System.out.println("我是一个单例模式!");
}
}
测试类:
public class Test {
public static void main(String[] args) {
System.out.println(Person.PERSON);
Person.PERSON.show();
}
}
输出结果:
Person{name = 酋长, age = 21} 我是一个单例模式!