抽象
抽象abstract
abstract修饰符的理解
- 用 abstract 修饰的方法叫做抽象方法,不能有方法体
- 拥有抽象方法的类必须是抽象类
- 抽象类中可以存在不是抽象方法的普通方法
- 抽象类必须被继承
- 抽象方法必须在子类中重写
- 抽象类不能直接创建对象,需要通过子类对象进行调用
举个例子
假设我们定义了一个抽象类名字为动物(Animal),这个类必须需要继承才能使用,但是每个动物的吃的东西和运动方式不同,定义一个猫类继承动物类,在定义一个狗类继承动物类来实例化输出不同内容
抽象类的使用
抽象类的写法
public abstract class 类名{
}
例子:定义一个Animal类(抽象类)
/**
* @Classname : Animal
* @Description : TODO 抽象的动物类
* @Author : lin_refuel@qq.com
*/
public abstract class Animal {
//定义一个成员变量
private String name; //名字
//动物类无参构造方法
public Animal() {
}
//带一个参数的构造方法,初始化成员变量
public Animal(String name) {
this.name = name;
}
//获取私有成员变量name值的方法
public String getName() {
return name;
}
//对私有成员变量进行赋值的方法
public void setName(String name) {
this.name = name;
}
//动物类的抽象方法,抽象类不能带有方法体
public abstract void eat();//吃
//动物类抽象方法,运动方式
public abstract void sport();
//抽象类中可以写非抽象的成员方法
public void sleep(){
System.out.println(this.name + "会睡觉");
}
}
上面抽象类中,带有abstrcat修饰符的方法是抽象方法,抽象方法必须在抽象类中写,eat方法和sport方法是抽象方法没有方法体,抽象类里面可以写非抽象类方法例如:sleep方法
抽象方法没有办法实例化,只能被继承,定义一个Cat类来继承 Animal类,进行使用
package demo01;
/**
* @Classname : Cat
* @Description : TODO 猫类 - 继承Animal类
* @Author : lin_refuel@qq.com
*/
public class Cat extends Animal {
//定义一个无参的构造方法
public Cat() {
}
//定义构造方法,该构造方法可以初始化Animal类私有成员变量name的值
public Cat(String name) {
super(name);
}
//重写Animal类里面的eat方法
@Override
public void eat() {
//Cat类中没有定义同类型的成员方法,这里使用this.getName()进行访问
System.out.println(this.getName() + "会吃鱼");
}
//重写Animal类里面的Sport方法
@Override
public void sport() {
System.out.println(this.getName() +"会上树");
}
}
Animal类的抽象方法必须在被继承的类里面进行重写,例如:上面Cat类里面重写了eat和sport方法
定义一个Dog类和上面的Cat类大体相同
package demo01;
/**
* @Classname : Dog
* @Description : TODO - 狗类
* @Author : lin_refuel@qq.com
*/
public class Dog extends Animal{
//无参构造方法
public Dog() {
}
//带有一个参数的构造方法
public Dog(String name) {
super(name);
}
//重写Animal里面eat方法
@Override
public void eat() {
System.out.println(this.getName() + "会吃骨头");
}
//重写Animal里面的sport方法
@Override
public void sport() {
System.out.println(this.getName() + "会帮助失明的人");
}
}
通过实例化Cat类和Dog类,调用抽象类中的方法和变量
package demo01;
/**
* @Classname : AnimalTest
* @Description : TODO 动物测试类
* @Author : lin_refuel@qq.com
*/
public class AnimalTest {
public static void main(String[] args) {
//实例化Cat
Cat cat = new Cat("波斯猫");
//调用cat对象中重写后的eat和sport方法
cat.eat();
cat.sport();
//调用cat继承Animal类的sleep方法
cat.sleep();
System.out.println("=====================");
Dog dog = new Dog("导盲犬");
//调用dog对象里面重写的eat和sport方法
dog.eat();
dog.sport();
//调用dog继承Animal中父类成员方法sleep
dog.sleep();
}
}
运行结果:
接口
接口interface是一种公共的规范,是一种引用数据类型,个人简单理解为一种特殊的类
接口的定义和使用
接口的定义方式
public interface 类名{
//成员
}
接口里面两个成员 常量和方法
接口里面的成员-------常量
格式:public static final 常量名称 = 值
注意事项:
- final 修饰的变量不能改变
- 接口中的常量必须赋值
- 常量使用大写字母、匈牙利命名法(OPEN_DOOR)
接口里面的成员-------抽象方法
接口中的抽象方法必须用public abstract
修饰(可以省略),实现接口的类中必须重写接口中所有的抽象方法
接口的使用方式
通过子类实现接口
例子:猫和狗
定义一个名字为Animal的接口
package com.xs.demo01;
/**
* @Classname : Animal
* @Description : TODO 动物类
* @Author : lin_refuel@qq.com
*/
public interface Animal {
//接口里面的成员--常量
//常量必须进行赋值
public static final String NAME = "动物";
//接口里面的方法只能是抽象方法,因为接口类也是抽象类嘛
public abstract void eat();
public abstract void sport();
//前面的public abstract 可以省去,因为接口里面定义方法默认是抽象方法
void sleep();
}
注意上面代码注释
定义一个名字为Cat的类实现Animal接口类
package com.xs.demo01;
/**
* @Classname : Cat
* @Description : TODO - 猫类-实现Animal类
* @Author : lin_refuel@qq.com
*/
public class Cat implements Animal {
//抽象方法必须重写
@Override
public void eat() {
System.out.println("猫会吃鱼");
}
//@Override:注解的意思,可以提示我们错误信息,例如重写时方法名不对,返回值不对
@Override
public void sport() {
System.out.println("猫会抓老鼠");
}
@Override
public void sleep() {
System.out.println("猫喜欢趴着睡");
}
}
注意上面代码注释
定义一个 Dog类,同样实现Animal接口
/**
* @Classname : Dog
* @Description : TODO 狗类 - 实现Animal接口
* @Author : lin_refuel@qq.com
*/
public class Dog implements Animal{
@Override
public void eat() {
System.out.println("狗会吃骨头");
}
@Override
public void sport() {
System.out.println("狗可以帮助警察");
}
@Override
public void sleep() {
System.out.println("狗哪里都可以进行睡眠");
}
}
测试类
/**
* @Classname : AnimalTest
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class AnimalTest {
public static void main(String[] args) {
//实例化Cat类
//访问Animal接口里面的常量的方式,类名.常量名
System.out.println(Cat.NAME); //动物
Cat cat = new Cat();
//访问对象里面的方法
cat.eat();
cat.sleep();
cat.sport();
System.out.println("================");
//实例化Dog类
Dog dog = new Dog();
//通过Dog类访问接口Animal里面的常量
System.out.println(Dog.NAME); //动物
//访问对象里面的方法
dog.eat();
dog.sleep();
dog.sport();
}
}
运行结果:
interface和abstract联合使用
如果接口的实现类没有重写接口里面的抽象方法,那么这个类就是一个抽象类
例子:
定义了一个接口类Animal
package com.xs.demo02;
/**
* @Classname : Animal
* @Description : TODO 一个名字为动物的接口
* @Author : lin_refuel@qq.com
*/
public interface Animal {
//定义一个常量
public static final String name = "动物";
//定义几个抽象方法,接口中默认是抽象方法,可以省略public abstract
void eat();
void sleep();
void sport();
}
定义一个抽象接口类Person实现接口
/**
* @Classname : Person
* @Description : TODO 人类 - 实现Animal接口
* @Author : lin_refuel@qq.com
*/
// 抽象类里面可以不用重写接口里面的抽象方法
public abstract class Person implements Animal{
//抽象的接口实现类可以有自己的非抽象方法
public void show(){
System.out.println("我是一名老师");
}
//也能写自己的抽象方法
public abstract void moon();
}
定义一个子类Teacher类继承Person类
/**
* @Classname : Teacher
* @Description : TODO 教师类 - 继承Person类
* @Author : lin_refuel@qq.com
*/
//抽象类不能实例化对象,只能通过定义一个子类来继承来调用
public class Teacher extends Person{
//子类继承抽象类,如果抽象类是接口实现类,那么子类里面一定要重写实现类里面的抽象方法
@Override
public void eat() {
System.out.println("老师喜欢吃馒头");
}
@Override
public void sleep() {
System.out.println("老师偶尔会晚睡");
}
@Override
public void sport() {
System.out.println("老师喜欢晨跑");
}
@Override
public void moon() {
System.out.println("我是Person类中的构造方法,在Teacher中进行重写");
}
测试类-调用接口里面的方法和常量
/**
* @Classname : test
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化Teacher类
Teacher teacher = new Teacher();
//继承的接口实现类的子类类名.常量名字,也可以访问接口里面的常量
System.out.println(Teacher.name);
//调用接口里的方法和继承的方法
teacher.eat();
teacher.sleep();
teacher.sport();
teacher.show();
teacher.moon();
}
}
运行结果:
接口中的default方法
个人理解:就是可以对接口里面的抽象方法进行一个升级
-
格式:
public default 返回值类型 方法名(参数列表){方法体}
-
注意事项:
-
用于接口升级
-
通过实现类的对象可以直接调用
-
默认方法也可以在实现类中进行重写
-
Java8.0 以上版本(包含)
-
举个例子:猫和狗都会进化,进化成为可以说话的动物,目前来说显然不太可能,
例子:我起名字为猫进化论
注意代码注释!!!
demo:
定义一个Animal接口
package com.xs.demo03;
/**
* @Classname : Animal
* @Description : TODO 动物
* @Author : lin_refuel@qq.com
*/
public interface Animal {
//定义一个接口常量,可以省去前面的public static final
String TYPE = "动物";
//定义三个动物的功能
void eat();
void sleep();
void sport();
//定义一个默认的方法,默认方法必须要有一个方法体
//默认方法可以升级接口类里面抽象方法,
//例如每个抽象类方法里面都添加一个功能,添加下面的话,只需要调用下面的默认方法就好了
public default void language(){
System.out.println(TYPE + "进化到会说人类语言");
}
//定义第二个进化功能
public default void handsAndFeet(){
System.out.println("手和脚");
}
}
定义一个Cat类实现接口
/**
* @Classname : Cat
* @Description : TODO 猫类 - 实现Animal接口
* @Author : lin_refuel@qq.com
*/
public class Cat implements Animal{
//实现接口类不是抽象类,必须要重写Animal类里面的抽象方法
@Override
public void eat() {
//通过调用Animal里面的默认方法对Cat类里面的重写后的eat方法进行升级
//调用接口里面默认方法的方式,this.默认方法名字
this.language();
System.out.println("猫:主人我想吃鱼了");
}
@Override
public void sleep() {
this.language();
System.out.println("猫:喵~ 主人晚安");
}
//Animal类里面的默认方法支持重写
//重写handsAndFeet方法
@Override
public void handsAndFeet() {
System.out.println(Cat.TYPE + "进化出手和脚");
}
@Override
public void sport() {
//调用没有被重写的Animal里面默认方法handsAndFeet()的方式如下
Animal.super.handsAndFeet();
//调用重写后的默认方法,方法如下
this.handsAndFeet();
System.out.println("猫:主人带我去跑步吧");
}
}
测试进化
package com.xs.demo03;
/**
* @Classname : test
* @Description : TODO 测试类 - 猫是否进化
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化一个猫类
Cat cat = new Cat();
//调用cat里面的方法
cat.eat();
System.out.println("=======");
cat.sleep();
System.out.println("========");
cat.sport();
}
}
注意注释
需要注意:接口里面的默认方法只能在实现接口类里面重写抽象方法里面的调用,实例化实现接口类里面也可以通过对象名字.默认方法名字调用不过没啥意义
运行结果:
一个类实现多个接口
写法
public class 类名 implements 接口名1,接口名2{
/*......*/
}
demo
定义一个名字为Teacher的接口
package com.xs.demo04;
/**
* @Classname : Teacher
* @Description : TODO 教师
* @Author : lin_refuel@qq.com
*/
public interface Teacher {
//定义一个抽象方法,讲课
void teach();
}
定义一个名字为My的接口
package com.xs.demo04;
/**
* @Classname : My
* @Description : TODO - MY
* @Author : lin_refuel@qq.com
*/
public interface My {
//定义一个抽象方法
void learn();
}
定义一个名字为xs作为实现两个接口的类
package com.xs.demo04;
/**
* @Classname : xs
* @Description : TODO
* @Author : lin_refuel@qq.com
*/
public class xs implements Teacher,My{
private String name;//姓名
//一个参数的构造方法
public xs(String name) {
this.name = name;
}
// 一个类实现两个接口,必须重写接口里面的抽象方法,除非这个类是抽象类
//重写 My接口里面的learn()方法
@Override
public void learn() {
System.out.println(this.name + "学习java");
}
//重写 Teacher接口里面的teach()方法
@Override
public void teach() {
System.out.println(this.name + "跟冰哥学习java");
}
}
注意上面接口实现类的注释和接口实现类实现两个接口的写法
测试类
package com.xs.demo04;
/**
* @Classname : test
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化对象
xs xs = new xs("小肆");
//调用对象xs里面的重写的抽象方法
xs.teach();
xs.learn();
}
}
运行结果:
一个接口继承多个接口
java中继承是单继承,但是接口可以继承多个接口,可以更加方便吧
接口多继承的写法
public interface 接口名 extends 接口名1, 接口名2{
/*......*/
}
- 注意事项:
- 如果 default 方法重名,需要重写 default 方法(子接口必须针对 default 方法进行重写,不能省略 default)
demo:
定义一个Teacher的接口
package com.xs.demo05;
/**
* @Classname : Teacher
* @Description : TODO 教师接口
* @Author : lin_refuel@qq.com
*/
public interface Teacher {
//接口功能,抽象方法
public abstract void teach();
}
定义一个名字为Programmer的接口
package com.xs.demo05;
/**
* @Classname : Programmer
* @Description : TODO 程序员接口
* @Author : lin_refuel@qq.com
*/
public interface Programmer {
//定义接口功能,抽象方法
void programming();
}
定义一个接口,继承上面的两个接口,注意接口继承多个接口的写法,
package com.xs.demo05;
/**
* @Classname : TeachersAndProgrammerslpmn
* @Description : TODO 老师和程序员的继承接口
* @Author : lin_refuel@qq.com
*/
public interface TeachersAndProgrammerslpmn extends Teacher,Programmer {
}
定义一个类,作为实现上面继承两个接口的接口实现类,
package com.xs.demo05;
/**
* @Classname : TandP
* @Description : TODO 老师和程序员继承接口实现类
* @Author : lin_refuel@qq.com
*/
public class TandP implements TeachersAndProgrammerslpmn{
//定义实现类里面的变量
private String name;
//无参数的构造方法
public TandP() {
}
//带参数的构造方法
public TandP(String name) {
this.name = name;
}
//重写抽象类方法
@Override
public void teach() {
System.out.println(this.name + "会讲课");
}
//重写抽象方法
@Override
public void programming() {
System.out.println(this.name + "会编程");
}
}
测试类
package com.xs.demo05;
/**
* @Classname : test
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化对象
TandP tandP = new TandP("冰哥");
//调用重写后的抽象方法
tandP.programming();
tandP.teach();
}
}
运行结果:
接口多继承时的default方法
接口多继承时,升级需要通过default方法
- 注意事项:
- 如果 default 方法重名,需要重写 default 方法(子接口必须针对 default 方法进行重写,不能省略 default)
demo : 接口多继承时默认方法重名和调用被继承的默认方法时的例子
定义一个名字为Teacher的接口
package com.xs.demo06;
/**
* @Classname : Teacher
* @Description : TODO 教师接口
* @Author : lin_refuel@qq.com
*/
public interface Teacher {
//定义一个抽象方法
void teach();
//定义一个默认方法
public default void job(){
System.out.println("老师的职责是讲课");
}
}
定义一个名字为Programmer的接口
package com.xs.demo06;
/**
* @Classname : Programmer
* @Description : TODO 程序员接口
* @Author : lin_refuel@qq.com
*/
public interface Programmer {
//定义一个抽象方法
void encode();
//定义一个抽象方法
public default void job(){
System.out.println("程序员会编程");
}
}
定义一个接口,继承上面的Teachear和Programmer接口,注意下面代码的注释
package com.xs.demo06;
/**
* @Classname : ProgrammerTeacher
* @Description : TODO 继承Teacher和 Programmer接口的接口
* @Author : lin_refuel@qq.com
*/
public interface ProgrammerTeacher extends Teacher,Programmer{
//接口继承多个接口时,被继承的多个接口默认方法不重名,这个不用重写
//接口继承多个接口时,被继承的接口中有同名的默认方法,必须在继承的接口里面进行重写
//Teacher和Programmer接口里面都有一个名字为job的默认方法,下面进行重写
@Override
public default void job() {
//只能在被重写的默认方法里面调用不被重写的默认方法,调用不被重写的被继承的默认方法的方式如下
//调用方式,被继承的接口名字.super.被继承的默认方法名字
Teacher.super.job();//调用Teacher接口里面的默认方法job
Programmer.super.job(); //调用Programmer接口的默认方法job
System.out.println("==================================");
//重写被继承接口里面的名字为job的默认方法
System.out.println("程序员老师会讲课");
}
}
定义一个类,作为上面接口的实现类
package com.xs.demo06;
/**
* @Classname : implementTheClass
* @Description : TODO ProgrammerTeacher接口的实现类
* @Author : lin_refuel@qq.com
*/
public class implementTheClass implements ProgrammerTeacher{
//定义一个成员属性
private String name;
//定义无参构造方法
public implementTheClass() {
}
//定义带参数的构造方法
public implementTheClass(String name){
this.name = name;
}
//实现类必须重写接口里面的抽象方法
//重写Programmer里面的encode方法
@Override
public void encode() {
System.out.println(this.name + "会编程");
}
//重写Teacher里面的抽象方法
@Override
public void teach() {
System.out.println(this.name +"会讲课");
}
}
定义一个测试类,调用上面代码的中抽象方法,默认方法
package com.xs.demo06;
/**
* @Classname : test
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化implementTheClass类
implementTheClass implementTheClass = new implementTheClass("老邪");
//调用实现类里面的方法
implementTheClass.encode();
implementTheClass.teach();
System.out.println("============================================");
implementTheClass.job();
}
运行结果:
继承一个类时同时实现多个接口
一个子类继承父类,同时实现两个接口
写法
public class 子类名 extends 父类名 implements 接口名1, 接口名2{
/*......*/
}
注意事项:
- 子类中必须重写所有接口中的抽象方法
- 若父类是抽象类,同样需要重写父类中的抽象方法
- 父类中与接口中方法重名,优先执行父类中的方法
demo :小肆学习编程,想成为一个程序员
定义一个名字为Leran的接口
package com.xs.demo07;
/**
* @Classname : Learn
* @Description : TODO 学习接口
* @Author : lin_refuel@qq.com
*/
public interface Learn {
//定义一个抽象方法
void learn();//学习
//定义一个默认方法
default void job(){
System.out.println("学习编程");
}
}
定义一个名字为Programmer的接口
package com.xs.demo07;
/**
* @Classname : Programmer
* @Description : TODO 程序员接口
* @Author : lin_refuel@qq.com
*/
public interface Programmer {
//定义一个抽象方法
void encode();
//定义一个默认方法
default void job(){
System.out.println("想要成为一个程序员");
}
}
定义一个父类
package com.xs.demo07;
/**
* @Classname : Itlx
* @Description : TODO 父类 - 职业
* @Author : lin_refuel@qq.com
*/
public class Itlx {
//定义一个成员属性
private String name;//名字
//无参的构造方法
public Itlx() {
}
//带参数的构造方法
public Itlx(String name) {
this.name = name;
}
//返回一个name的值
public String getName() {
return name;
}
//初始化name的值
public void setName(String name) {
this.name = name;
}
}
定义一个子类-继承上面的父类,实现Learn接口和实现Programmer接口
package com.xs.demo07;
/**
* @Classname : itxs
* @Description : TODO 子类 - 继承Itlx类,同时作为实现Learn接口和Programmer接口的类
* @Author : lin_refuel@qq.com
*/
public class itxs extends Itlx implements Learn,Programmer{
//子类继承父类时,必须要调用父类当中的构造方法,
public itxs(String name) {
super(name);
}
//接口实现类,必须要重写接口里面的抽象方法
//重写 Learn,里面的抽象方法learn
@Override
public void learn() {
System.out.println(this.getName() + "学习java");
}
//重写 Programmer里面的抽象方法 encode
@Override
public void encode() {
System.out.println(this.getName() + "会写代码");
}
//子类实现两个接口类时,两个接口里面的默认方法重名,必须进行重写
@Override
public void job() {
//调用Learn里面的默认方法
Learn.super.job();
System.out.println("==============");
//调用Programmer里面的默认方法
Programmer.super.job();
System.out.println("==============");
//重写默认方法jop
System.out.println(this.getName() + "会记录学习过程");
}
}
测试类
package com.xs.demo07;
/**
* @Classname : test
* @Description : TODO 测试类
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化对象
itxs itxs = new itxs("小肆");
itxs.learn();
itxs.encode();
System.out.println("=====================");
itxs.job();
}
}
运行结果:
final的用法
final 的使用
- 修饰类:用 final 修饰的类不能被继承
- 修饰局部变量:用 final 修饰的局部变量一旦被赋值则不允许修改
- 修饰成员变量:用 final 修饰的成员变量必须初始化一个值
- 直接赋值
- 构造方法赋值
- 修饰方法:用 final 修饰的方法不能被重写
demo
定义一个用final修饰的类,名字为A类
package com.xs.demo08;
/**
* @Classname : A
* @Description : TODO 带有修饰符final的类
* @Author : lin_refuel@qq.com
*/
public final class A {
}
定义一个名字为B的类继承A类,注意注释
package com.xs.demo08;
/**
* @Classname : B
* @Description : TODO 验证是否可以继承A类
* @Author : lin_refuel@qq.com
*/
//带有修饰符final的类不能被继承
public class B /*extends A*/{
//定义一个方法
public void month(){
//定义一个带有修饰符final的局部变量
final int a = 10;//final的变量的值不能被修改
//a=100;
}
}
定义一个C类继承上面的B类,注意注释
package com.xs.demo08;
/**
* @Classname : C
* @Description : TODO
* @Author : lin_refuel@qq.com
*/
public class C extends B{
//定义一个带有修饰符final的成员变量
//带有修饰符final的成员变量两种赋值方式
//1.直接赋值,a = 10;
public final int a ;
//2.通过构造方法进行赋值
public C(int a) {
this.a = a;
}
}
test测试类
package com.xs.demo08;
/**
* @Classname : test
* @Description : TODO
* @Author : lin_refuel@qq.com
*/
public class test {
public static void main(String[] args) {
//实例化对象C
C c = new C(120);//调用C类里面的构造方法,给成员变量a赋值
System.out.println(c.a);
}
}
运行结果: