Java面向对象
编辑时间:2021/03/09
读完本节:大概花费60分钟,共6188词
1.抽象类与抽象方法
-
解释:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。又是一个父类设计的非常抽象,以至于没有具体的实例,这样的类叫做抽象类。
-
abstract(抽象的):可以用来修饰的结构:类、方法
-
abstract修饰类:抽象类
- 抽象类不能实例化
- 抽象类中一定构造器,至少有"super();"。便于子类的调用(涉及子类对象的实例化过程)
- 开发中都会提供抽象类的子类,让子类对象实例化,完成相关的操作
-
abstract修饰方法:抽象方法
-
抽象方法只有方法的声明,没有方法体
-
包含抽象方法的类,一定是一个抽象类,反之抽象类中可以没有抽象方法。
-
若子类重写了父类中所有的抽象方法,则次子类可以实例化
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
-
-
abstract使用上的注意点:
- abstract不能用来修饰:属性、构造器等结构
- abstract不能用来修饰私有化方法、静态方法、final的方法、final的类
-
抽象类的应用:
-
编写一个Employee类,声明为抽象类
包含如下三个属性:name,id,salary
提供必要的构造器和抽象方法work()
对于Manager类来说,他既是员工还具有奖金(bonus)的属性
使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性的访问。
Employee
package abstracttest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 20:47 * @Description: */ public abstract class Employee { private String name; private int id; private double salary; public Employee(){ } public Employee(String name, int id, double salary) { this.name = name; this.id = id; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public abstract void work(); }
Manager
package abstracttest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 20:47 * @Description: */ public class Manager extends Employee { private double bonus; public Manager(double bonus) { this.bonus = bonus; } public Manager(String name, int id, double salary,double bonus){ super(); this.bonus = bonus; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public void work(){ System.out.println("经理工作是安排工作"); } }
CommonEmployee
package abstracttest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 20:47 * @Description: */ public class CommonEmployee extends Employee{ public CommonEmployee(String name, int id, double salary){ super(); } public void work(){ System.out.println("普通工人的工作是完成上级安排的工作"); } }
AbstractTest
package abstracttest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:01 * @Description: */ public class AbstractTest { public static void main(String[] args){ Manager m1 = new Manager("biggy", 1001, 10000,500); m1.work(); CommonEmployee ce1 = new CommonEmployee("bobo", 1002, 8000); ce1.work(); } }
-
-
抽象类的匿名子类
匿名子类的概念与匿名对象类似,即不显示的定义子类的名称,直接在程序中使用这个子类,同样的在其作用域内可以重复使用,出了作用域只能用一次
同样的匿名子类需要重写父类中被abstract修饰的抽象方法
Creature
package abstractAnonymousTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:13 * @Description: */ public abstract class Creature { public abstract void breath(); }
Person
package abstractAnonymousTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:13 * @Description: */ public abstract class Person extends Creature{ private String name; private char sex; public Person(){ super(); } public Person(String name, char sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } // @Override // public void breath() { // System.out.println("人可以呼吸"); // } }
Student
package abstractAnonymousTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:13 * @Description: */ public class Student extends Person{ private int studentId; public Student(int studentId) { this.studentId = studentId; } public Student(String name, char sex, int studentId) { super(name, sex); this.studentId = studentId; } public int getStudentId() { return studentId; } public void setStudentId(int studentId) { this.studentId = studentId; } @Override public void breath() { System.out.println("学生的肺活量大"); } }
PersonTest
package abstractAnonymousTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:14 * @Description: */ public class PersonTest { public static void main(String[] args){ //1. 非匿名类的非匿名对象 Student student1 = new Student("张三",'男',1001); String name = method1(student1); System.out.println(name); //2. 非匿名类的匿名对象 String name1 = method1(new Student("李四",'男',1002)); System.out.println(name1); //3. 匿名类的非匿名对象 Person person = new Person("王五",'男') { @Override public void breath() { } }; String name2 = method1(person); System.out.println(name2); //4. 匿名类的匿名对象 String name3 = method1(new Person("赵六",'男') { @Override public void breath() { } }); System.out.println(name3); } public static String method1(Person person){ return person.getName(); } }
-
多态的应用:模板方法设计模式(TemplateMethod)
-
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造、但子类总体上会保留抽象类的行为方式
-
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这个时候就可以把不确定的部分暴露出去,让子类去实现。换句话说,在软件开发中实现一个算法时,整体的步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变的部分可以抽象出来,供不同子类实现。这是一种模板模式。
-
使用模板方法设计模式计算输出1-1000之间素数的个数的时间
package templateTest; /** * @Author: xuehai.XUEi * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 21:57 * @Description: 通过Template类的spendTime方法获取某段代码的执行时间 */ public class TemplateTest { public static void main(String[] args){ //使用匿名对象实例化 new SubTemplate().spendTime(); } } abstract class Template{ //1. 实现父类的通用模板 public void spendTime(){ long start = System.currentTimeMillis() ; //不确定的部分 code(); long end = System.currentTimeMillis(); System.out.println("花费时间为:" + (end - start) + "ms"); } //2. 抽象出易变部分,让子类实现抽象的方法 public abstract void code(); } //3. 实现易变部分的子类 class SubTemplate extends Template{ @Override //4. 重写模板中易变的部分的抽象方法 public void code(){ //输出1000以内的素数 for(int i = 2;i <= 1000; i++){ boolean isFlag = true;//标记素数 //判断能否被除尽 for(int j = 2;j <= Math.sqrt(i);j++){ if(i % j == 0){ isFlag = false;//不是素数将旗帜设为false break; } } if(isFlag){ System.out.println(i); } } } }
-
模板方法的应用场景:b数据库访问的封装
- JUnit单元测试
- JavaWeb的Servlet中关于doGet和doPost方法的调用
- Hibernate中模板程序
- Spring中JDBCTemplate、HibernateTemplate等
-
-
抽象类的练习:
-
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象生日,则将该雇员的工资增加100元
-
定义一个Employee类,包含private成员变量:name、number、birthday,其中birthday为MyDate类的对象;abstract方法earning(); toStirng()方法输出对象的name、number、birthday
-
定义一个MyDate类,包含private成员变量:year、month、day;toDateString()方法返回日期对应的字符串
-
定义SalaryEmployee类继承类Employee类,实现按月计算工资的员工处理,包括private成员变量:monthlySalary;实现父类的抽象方法earning(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name、number、birthday
-
参照SalaryEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理,该类包括:private成员变量wage、hour;实现父类的抽象方法earning(),该方法返回wage*hour值;toString()方法输出员工类型及员工的name、number、birthday
-
定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象的生日。当键盘输入本月月份值时,如果该月是某个Employee的生月,还要输出增加工资的信息
Employee
package payrollSystem; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 22:33 * @Description: */ public abstract class Employee { private String name;//员工姓名 private int number;//员工工号 private MyDate birthday;//员工生日 public Employee() { } public Employee(String name, int number, MyDate birthday) { this.name = name; this.number = number; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } public abstract double earning(); public String toString(){ return "name = " + name + ", number = " + number + ", birthday = " + birthday.toDateString(); } }
MyDate
package payrollSystem; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 22:35 * @Description: */ public class MyDate { private int year; private int month; private int date; public MyDate(int year, int month, int date) { this.year = year; this.month = month; this.date = date; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDate() { return date; } public void setDate(int date) { this.date = date; } public String toDateString(){ return year + "年" + month + "月" + date + "日"; } }
SalaryEmployee
package payrollSystem; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 22:43 * @Description: */ public class SalaryEmployee extends Employee { private double monthlySalary; public SalaryEmployee(double monthlySalary) { this.monthlySalary = monthlySalary; } public SalaryEmployee(String name, int number, MyDate birthday, double monthlySalary) { super(name, number, birthday); this.monthlySalary = monthlySalary; } public double getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(double monthlySalary) { this.monthlySalary = monthlySalary; } @Override public double earning() { return monthlySalary; } @Override public String toString(){ return "SalaryEmployee:[ " + super.toString() + " ]"; } }
HourlyEmployee
package payrollSystem; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 22:47 * @Description: */ public class HourlyEmployee extends Employee { private double wage; private int hour; public HourlyEmployee(double wage, int hour) { this.wage = wage; this.hour = hour; } public HourlyEmployee(String name, int number, MyDate birthday, double wage, int hour) { super(name, number, birthday); this.wage = wage; this.hour = hour; } public double getWage() { return wage; } public void setWage(double wage) { this.wage = wage; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } @Override public double earning() { return wage*hour; } @Override public String toString(){ return "HourlyEmployee:[ " + super.toString() + " ]"; } }
PayrollSystem
package payrollSystem; import java.util.Calendar; import java.util.Scanner; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/9 22:49 * @Description: */ public class PayrollSystem { public static void main(String[] args){ int MAX_EMPLOYEES = 2; // //方式一:获取用户输入的月份 // System.out.println("输入月份:" ); // Scanner scanner = new Scanner(System.in); // int month = scanner.nextInt(); //方式二:使用Calender类获取当前月份,Calender类的月份month是从0开始的 Calendar calendar = Calendar.getInstance(); int month = calendar.get(Calendar.MONTH); // System.out.println(month); Employee[] employees = new Employee[MAX_EMPLOYEES]; employees[0] = new SalaryEmployee("biggy", 1001, new MyDate(1999, 12, 12), 25000); employees[1] = new HourlyEmployee("bobo", 1002, new MyDate(2020, 6, 26), 10, 240); for(int i = 0;i < employees.length;i++){ System.out.println(employees[i].toString()); System.out.println("月工资为:" + employees[i].earning()); //if该月生日,输出提示 if((month + 1) == employees[i].getBirthday().getMonth()){ System.out.println("生日快乐!好耶,这个月加薪100!"); } } } }
-
2.接口
-
接口使用interface来定义,在Java中接口和类是两个并列的结构,定义接口就是定义接口中的成员
-
jdk7以前,只能定义全局常量和抽象方法
- 全局常量:public static final,在接口中可以省略不写
- 抽象方法:public abstract
jdk8以后,除了可以定义全局常量和抽象方法外,还可以定义静态方法、默认方法
-
静态方法:使用static关键字修饰,可以通过接口直接调用静态方法且仅能通过接口来调用,并执行其方法体。比如:Collection/Collections;Path/Paths这样成对的接口和类
public static void method(){}
-
默认方法:默认方法使用default关键字修饰。可以通过实现类对象来调用。在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性
public default void method(){}
//或
default void method(){}
-
如果子类(或称实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么在子类没有重写此方法的情况下,默认调用的时父类中同名同参数的方法(类优先原则)
-
接口不能定义构造器,接口不能实例化。
-
接口在开发中,通过类去实现(implements)的方式来使用。如果实现类覆盖了接口中所有的抽象方法,则此实现类就可实例化;如果实现类没有覆盖接口中的所有抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口,这个特性弥补了Java类单继承的局限性,接口与接口之间可以继承,而且可以多继承。
class AA extends BB implements CC,DD,EE
-
接口中的默认方法:
-
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同实现了这两个接口的同时会出现接口冲突。解决方法:实现类必须覆盖接口中同名同参数的方法,来解决冲突
-
若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
-
调用子类(实现类)中与父类同名同参数的方法直接使用“方法名()”的方式调用
调用父类中非抽象的方法使用“super.方法名()”实现调用
调用接口中的默认方法:“接口名.super.方法名()”实现调用
-
接口可以继承接口,抽象类可以实现(implements)接口,抽象类可以继承非抽象的类
-
-
接口的使用体现了多态性,接口实际上可以看成一种规范
package interfaceTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 9:00 * @Description: */ public class InterfaceTest { public static void main(String[] args){ //1. 创建接口的非匿名实现类的非匿名对象 Computer computer = new Computer(); Flash flash = new Flash(); computer.transforData(flash); //******************************************** System.out.println("*********************************"); //2. 创建接口的非匿名实现类的匿名对象 computer.transforData(new Flash()); //******************************************** System.out.println("*********************************"); //3. 创建接口的匿名实现类的非匿名对象 USB phone = new USB() { @Override public void start() { System.out.println("手机开始工作"); } @Override public void stop() { System.out.println("手机停止工作"); } }; computer.transforData(phone); //******************************************** System.out.println("*********************************"); //4. 创建接口的匿名实现类的匿名对象 computer.transforData(new USB() { @Override public void start() { System.out.println("相机开始工作"); } @Override public void stop() { System.out.println("相机停止工作"); } }); } } class Computer{ //该方法的形参是一个接口 public void transforData(USB usb){//USB usb = new Flash(); usb.start(); System.out.println("具体传输数据的细节"); usb.stop(); } } //接口定义了俩个抽象方法 interface USB{ void start(); void stop(); } //具体实现类实现接口定义的抽象方法 class Flash implements USB{ @Override public void start() { System.out.println("闪盘开始工作"); } @Override public void stop() { System.out.println("闪盘结束工作"); } } abstract class Printer implements USB{ @Override public void start() { System.out.println("打印机开始工作"); } //若接口中的所有抽象方法没有被重新覆盖,则实现接口的方法仍然是抽象的 // @Override // public void stop() { // System.out.println("打印机停止工作"); // } }
-
抽象类和接口的异同:
-
相同点:抽象类和接口都不能实例化;都可以包含抽象方法;类和接口都可以实现多实现,即一个父类可以有多个子类,一个接口可以有多个实现方法
-
不同点:
-
对于抽象类:
abstract修饰类:抽象类抽象类不能实例化抽象类中一定构造器,至少有"super();"。便于子类的调用(涉及子类对象的实例化过程)开发中都会提供抽象类的子类,让子类对象实例化,完成相关的操作
abstract修饰方法:抽象方法抽象方法只有方法的声明,没有方法体包含抽象方法的类,一定是一个抽象类,反之抽象类中可以没有抽象方法。若子类重写了父类中所有的抽象方法,则次子类可以实例化若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
-
对于接口:
在Java7及以下:只能定义全局常量和抽象方法
在Java8及以上:除了可以定义全局常量和抽象方法外,还可以定义静态方法、默认方法
在Java9及以上:可以在接口中定义私有方法
接口的发展趋势向类靠近
-
类只能单继承,而接口可以实现多继承。
-
-
3.设计模式之代理模式
-
概述:代理模式是Java开发中使用较多的一种结构型设计模式。代理设计就是为其他对象提供一种代理以空着对这个对象的访问。
-
应用场景:
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法的调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要在加载真实对象
-
分类:静态代理和动态代理
静态代理:静态定义代理类
动态代理:动态生成代理类(JDK自带的动态代理,需要应用反射)
-
静态代理示例:
package proxyTest; import sun.nio.ch.Net; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 9:46 * @Description: 代理模式 */ public class NetWorkTest { public static void main(String[] args){ Server server = new Server(); ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse(); } } interface NetWork{ void browse();//省略了public void } //被代理类 class Server implements NetWork{ @Override public void browse() { System.out.println("真实的服务器访问网络"); } } //代理类 class ProxyServer implements NetWork{ private NetWork work; //constructor public ProxyServer(NetWork work){//NetWork work = new Server(); this.work = work; } public void check(){ System.out.println("联网之前的检查工作"); } @Override public void browse() { check(); //调用被代理类中的方法 work.browse(); } }
4.设计模式之工厂设计模式
-
接口的应用:工厂模式
-
工厂模式:工厂模式是创建型模式,实现了创建者和调用者的分离,即及将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
-
面向对象中有关工厂模式的设计原则
OCP(开闭原则,Open-Close Principle):一个软件的实体应当对外开放,对修改关闭,控制需求变动风险,缩小维护成本
DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。如果A中关联B,那么尽量使得B实现某个接口,然后A与接口发生关系,不与B实现类发生关联关系。面向抽象编程,解耦调用和被调用者
LOD(迪米特法则,Low Of Demeter):要求尽量的封装、尽量的独立、尽量的使用低级别的访问修饰符。要求类之间的直接练习尽量的少,两个类的访问,通过第三个中介类来实现
-
工厂模式的分类:
-
简单工厂模式:用来生产同一等级结构中的任意产品。调用者只要知道要什么,如何创建不需要知道,直接从工厂拿。简单工厂模式也叫静态工厂模式,就是工厂类一般使用的是静态方法,通过接收的参数不同来返回不同的实例对象。缺点:对于新增加的产品,不修改代码的话是无法扩展的,这违反了开闭原则(对外扩展开放,对修改封闭)。
-
工厂方法模式:用来生产同一等级结构中的固定产品。避免了简单工厂模式不完全满足OCP的缺点。工厂方法模式和简单工厂模式最大的不同在于:**简单工厂模式只有一个工厂(对于一个项目或者一个独立的模块而言),而工厂方法模式有一组实现了相同接口的工厂类。**这样在简单工厂模式里集中的在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
简单工程模式和工厂方法模式并没有真正的避免了代码的改动,在简单工厂模式中,新产品的加入要修改工厂角色中的判断语句;而在工厂方法模式中,要么将判断逻辑留在抽象工厂角色中,要么客户在程序中将具体的工厂角色写死,而且产品对象创建条件的改变必然会引起工厂角色的修改。面对这种情况,Java的反射机制与配置文件的巧妙结合突破了限制,在Spring中完美体现。
-
抽象工厂模式:用来生产不同产品族的全部产品。**抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。**而且抽象工厂模式是三个工厂模式中最为抽象、最具有一般性的。抽象工厂模式给客户端提供一个接口,可以创建多个产品族中的产品对象
使用抽象工厂模式需要满足以下条件:
- 系统中由多个产品族,而系统一次只可能消费其中一族产品
- 同属于一个产品族的产品以其使用
举例:
无工厂模式:
package factoryTest.none; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 10:15 * @Description: no factory pattern */ interface Car{ void run(); } class Audi implements Car{ @Override public void run() { System.out.println("Audi is running"); } } class BYD implements Car{ @Override public void run() { System.out.println("BYD is running"); } } public class NoFactory { public static void main(String[] args){ //创建者 Car a = new Audi(); Car b = new BYD(); //调用者 a.run(); b.run(); } }
简单工厂模式:
package factoryTest.simple; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 10:24 * @Description: 简单工厂模式 */ interface Car{ void run(); } class Audi implements Car{ @Override public void run() { System.out.println("Audi is running"); } } class BYD implements Car{ @Override public void run() { System.out.println("BYD is running"); } } //工厂类 class CarFactory{ //方式一: //判断输入想要创建类型,创建相应的类型的实例 //创建者 //仅有一个工厂 public static Car getCar(String type){ if("Audi".equals(type)){ return new Audi(); }else if("BYD".equals(type)){ return new BYD(); }else{ return null; } } // //方式二: // public static Car getAudi(){ // return new Audi(); // } // public static Car getBYD(){ // return new BYD(); // } } public class SimpleFactory { public static void main(String[] args){ //调用CarFactor类创建相应的实例化对象 Car a = CarFactory.getCar("Audi"); Car b = CarFactory.getCar("BYD"); //调用者 a.run(); b.run(); } }
工厂方法模式:
package factoryTest.method; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 10:40 * @Description: */ //Car接口 interface Car{ void run(); } //Car实现类 class Audi implements Car{ @Override public void run() { System.out.println("Audi is running"); } } class BYD implements Car{ @Override public void run() { System.out.println("BYD is running"); } } //工厂接口 interface Factory{ Car getCar(); } //两个工厂子类,根据实际需要,可以扩增子类实现OCP原则 class AudiFactory implements Factory{ //创建者 @Override public Audi getCar() { return new Audi(); } } class BYDFactory implements Factory{ //创建者 @Override public BYD getCar() { return new BYD(); } } public class MethodFactory { public static void main(String[] args){ //调用两个工厂子类的方法实现获取对象的实例 Car a = new AudiFactory().getCar(); Car b = new BYDFactory().getCar(); //调用者 a.run(); b.run(); } }
-
5.接口练习题
-
排错
interface A{ int x = 0; } class B{ int x = 1; } class C extends B implements A{ public void pX(){ System.out.println(x); } public static void main(String[] args){ new C().pX(); } } //*********************************** /* pX()方法中x指代不明 应该修改为 System.out.println(super.x);//1 或 System.out.println(A.x);//0 */
-
排错
interface Playable{ void play(); } interface Bounceable{ void play(); } interface Rollable extends Playable, Bounceable{ Ball ball = new Ball("PingPong"); } class Ball implements Rollable{ private String name; public String getName(){ return name; } public Ball(String name){ this.name = name; } public void play(){ ball = new Ball("Football"); System.out.println(ball.getName()); } } //******************************** /* 首先不建议在接口中定义同名的抽象方法,会造成覆盖时语义不清 其次,实现类Ball中的play方法中将一个新的对象赋给了ball,但是ball在接口Rollable中已经实例化 而且在接口中的Ball ball = new Ball("PingPong");前省略了public static final */
6.类的成员 - 内部类
-
概述:当一个事物的内部,还有一个部分需要完整的结构进行描述,而这个内部完整的结构由只为外部事物提供服务,那么这个内部完整的结构最好使用内部类
-
在Java中允许一个类定义在另一个类的内部,前者称为内部类,后者称为外部类
InnerClass一般用在定义它的类或语句块之内,在外部引用它时必须给出完整名称,InnerClass的名字不能与包含它的OutterClass类类名相同
-
内部类的分类:
成员内部类:static成员内部类、非static成员内部类;局部内部类(方法内、代码块内、构造器内);匿名内部类
-
成员内部类:
- 作为外部类的成员:可以调用外部类的结构;可以被static修饰;可以被四种不同的权限修饰
- 作为一个类:类内可以定义属性、方法、构造器等;可以被final修饰,表示此类不能被继承,不使用final则可以被继承;可以被abstract修饰
-
成员内部类和局部内部类,在编译以后,都会生成字节码文件。其各自的字节码命名格式如下:
成员内部类:外部类$内部类名.class
局部内部类:外部类$数字内部类名.class(处于不同外部类内不同位置,如方法内、代码块内、构造器内各内部类可以重名,数字用于区分不同位置的字节码文件)
-
如何实例化成员内部类?
如何在成员内部类中区分调用外部类的结构?
开发中局部内部类的使用?
package innerClassTest; /** * @Author: xuehai.XUE * @MailBox: xuehai.xue@qq.com * @Date: 2021/3/10 12:24 * @Description: * 1. 如何实例化成员内部类? * 2. 如何在成员内部类中区分调用外部类的结构? * 3. 开发中局部内部类的使用? */ public class InnerClassTest { public static void main(String[] args){ //1. 创建内部类的实例(静态成员内部类) Person.Dog dog = new Person.Dog(); dog.run(); //1. 创建内部类的实例(非静态成员内部类) Person p = new Person(); Person.Bird bird = p.new Bird(); bird.sing(); } } class Person{ String name; public void eat(){ System.out.println("people can eat"); } public static void read(){ System.out.println("people can read"); } //静态内部类 static class Dog{ String name; public Dog(){ } public void run(){ System.out.println("dog running"); } } //非静态内部类 class Bird{ String name; public Bird(){ } public void sing(){ System.out.println("bird singing"); //2. 在成员内部类中区分调用外部类的方法 //调用外部类的非静态方法 Person.this.eat(); //调用外部类的静态方法 read(); } //2. 在成员内部类中区分调用外部类的属性 public void display(String name){ System.out.println(name);//方法的形参 System.out.println(this.name);//内部类的属性 System.out.println(Person.this.name);//外部类的属性 } } public void method(){ //方法中的局部内部类 class AA{ } //代码块中的局部内部类 { class BB{ } } } //在构造器中的局部内部类 public Person(){ class CC{ } } //局部内部类的使用 public Comparable getComparable(){ //创建一个实现了Comparable接口的类:局部内部类 //方式一: // class MyComparable implements Comparable{ // // @Override // public int compareTo(Object o) { // return 0; // } // } // return new MyComparable(); //方式二: //创建了一个匿名内部类 return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; } }