该笔记基于B站视频尚硅谷Java入门视频教程
1,Day14复习
1.1 每天一考
1. static 修饰的属性,相较于实例变量,有哪些特别之处(>=3点)?
1.1 随着类的加载而加载;
1.2 早于对象的创建;
1.3 只要权限允许,可以通过”对象.static属性”的方式进行调用;
1.4 存在于方法区的静态域
2. final 可以用来修饰哪些结构,分别表示什么意思?
2.1 final可以修饰类、变量和方法;
2.2 当final修饰类时:类不能被继承。
2.3 当final修饰变量时:此时的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
final修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化
final修饰局部变量:尤其是使用final修饰形参时,表明该形参是一个常量。当我们调用此方法时,给常量形参赋一个实参,一旦以后,就只能在方法体中使用此形参,但不能进行重新赋值。
2.4当final修饰方法时:方法不能被子类重写 比如:Object类中的getClass()(获取当前对象所属的类)。
3. 代码实现单例模式的饿汉式 ?
饿汉式:
坏处:对象的加载时间过长好处:是线程安全的
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
System.out.println(bank1==bank2);//true
}
}
class Bank{
//1,私有化的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象必须声明为静态的
private static Bank instance =new Bank();
//3.提供公共的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
4. 代码实现单例模式的懒汉式 -目前还是线程不安全的。
懒汉式:
好处:延迟对象的创建坏处:目前来说线程不安全
//懒汉式实现:什么时候用什么时候造
public class SingletonTest2 {
public static void main(String[] args) {
Order order1=Order.getInstance();
Order order2=Order.getInstance();
System.out.println(order1==order2);
}
}
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类的对象,没有初始化
private static Order instance=null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
if (instance == null) {
instance=new Order();
}
return instance;
}
}
5. 类的属性赋值的位置有哪些?先后顺序为何?
- 默认初始化
- 显式初始化 、代码块中初始化
- 构造器中初始化
- 通过”对象.属性” 或”对象.方法”的方式赋值
2,抽象类与抽象方法(abstract)
引入:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
2.1,abstract关键字的使用
1.可以用来修饰:类、方法
2.具体的:
package Day15.Abstruct;
public class AbstractTest {
public static void main(String[] args) {
//1,一旦Person抽象了就不能被实例化
// Person p1=new Person();
// p1.eat();
}
}
abstract class Creature{
public abstract void breath();
}
abstract class Person extends Creature{
String name;
int age;
public Person(){
}
//2,抽象类中一定有构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//不是抽象方法
// public void eat(){
//
// }
//抽象方法
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
}
//方法2 抽象类直接用abstract修饰,若为非抽象类参考方法1
abstract class Student extends Person{//子类
public Student(String name ,int age){
}
//方法1 非抽象类需要重写所有的抽象方法
// public void eat(){
// System.out.println("学生应该多吃有营养的食物");
// }
@Override
public void breath(){
System.out.println("学生呼吸空气");
}
}
- abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性
- abstract修饰方法:抽象方法
- 抽象方法只方法的声明,没方法体
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所的抽象方法后,此子类方可实例化
- 若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
2.2,abstract使用上的注意点
- 1.abstract不能用来修饰:属性、构造器等结构
- 2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
2.3,抽象类的应用
思考
- 问题1:为什么抽象类不可以使用final关键字声明?
答:因为抽象类需要被子类继承,而final关键词不能被继承 - 问题2:一个抽象类中可以定义构造器吗?
答:抽象类中可以定义构造器,因为需要调用其中的属性。 - 问题3:是否可以这样理解:抽象类就是比普通类多定义了抽象方 法,除了不能直接进行类的实例化操作之外,并没有任何的不同?
答:可以,抽象类除了不能直接类的实例化,其他方面与普通的类没有差别
练习
练习2 编写一个Employee类,声明为抽象类, 包含如下三个属性:name,id,salary。 提供必要的构造器和抽象方法:work()。 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
请使用继承的思想,设计CommonEmployee类和Manager类,要求类 中提供必要的方法进行属性访问。
package Day15.Abstruct.ManagerTest;
public class EmployeeTest {
public static void main(String[] args) {
Manager manager=new Manager("库克",1001,5000,50000);
manager.work();
CommonEmployee commonEmployee=new CommonEmployee();
commonEmployee.work();
Employee manager1=new Manager("乔布斯",1002,5000,60000);
manager1.work();
}
}
package Day15.Abstruct.ManagerTest;
public abstract class Employee {
private String name;
private int id;
private double salary;
public Employee(){
super();
}
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
package Day15.Abstruct.ManagerTest;
public class Manager extends Employee{
private double bonus;//奖金
@Override
public void work(){
System.out.println("管理员工,提供公司运行的效率");
}
public Manager(double bonus) {
this.bonus = bonus;
}
public Manager(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
}
package Day15.Abstruct.ManagerTest;
public class CommonEmployee extends Employee{
@Override
public void work() {
System.out.println("员工生产产品");
}
}
2.4抽象类的匿名子类的使用
package Day15.Abstruct;
import javafx.concurrent.Worker;
public class PersonTest {
public static void main(String[] args) {
method(new Student());
//创建了一个匿名子类
Person p = new Person() {
@Override
public void eat() {
}
@Override
public void breath() {
}
};
method(p);
System.out.println("1111111111111111111111111");
method(new Person() {
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void breath() {
System.out.println("呼吸呼吸");
}
});
}
public static void method(Student s) {
}
public static void method(Person p) {
p.eat();
p.walk();
}
}
2.5 模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模
板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象
类的行为方式。
解决的问题:
- 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以 把不确定的部分暴露出去,让子类去实现。
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽
象出来,供不同子类实现。这就是一种模板模式
示例代码如下:
package Day15.Abstruct;
//计算某段代码花费的时间
public class TemplateTest {
public static void main(String[] args) {
Template t=new SubTemplate();
t.spendTime();
}
}
abstract class Template{
public void spendTime(){
long start=System.currentTimeMillis();
code();
long end=System.currentTimeMillis();
System.out.println("花费的时间为"+(end-start));
}
public abstract void code();
}
class SubTemplate extends Template{
public void code(){
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;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
2.6 课后题:
题目: 编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个
Employee对象的生日,则将该雇员的工资增加100元。 实验说明: (1)定义一个Employee类,该类包含:
private成员变量name,number,birthday,其中birthday 为MyDate类的对象;
abstract方法earnings(); toString()方法输出对象的name,number和birthday。
(2)MyDate类包含: private成员变量year,month,day ;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处
理。该类包括:private成员变量monthlySalary;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输
出员工类型信息及员工的name,number,birthday
(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的 员工处理。该类包括:
private成员变量wage和hour; 实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday。
(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各
类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类
型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。提示: //定义People类型的数组People c1[]=new People[10]; //数组元素赋值 c1[0]=new People(“John”,“0001”,20); c1[1]=new People(“Bob”,“0002”,19);
//若People有两个子类Student和Officer,则数组元素赋值时,可以使父类类型的数组元素指向子类。 c1[0]=new
Student(“John”,“0001”,20,85.0); c1[1]=new
Officer(“Bob”,“0002”,19,90.5)
package Day15.Abstruct.MyDate;
import java.util.Calendar;
import java.util.Scanner;
/**
* (5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各
* 类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类
* 型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本
* 月是某个Employee对象的生日,还要输出增加工资信息。
*/
public class PayrollSystem {
public static void main(String[] args) {
//方式一
// Scanner scanner=new Scanner(System.in);
// System.out.println("请输入当前月份:");
// int month=scanner.nextInt();
//方式二
Calendar calendar=Calendar.getInstance();
int month=calendar.get(Calendar.MONTH)+1;
Employee emps[]=new Employee[2];
for (int i = 0; i < emps.length; i++) {
}
emps[0]=new SalaryEmployee("张三", 1001, new MyDate(1992,2,28),23000);
emps[1]=new HourEmployee("李四",1002,new MyDate(1999,8,30),100,240);
// System.out.println(emps[0].toString());
// System.out.println(emps[1].toString());
for (int i = 0; i < emps.length; i++) {
System.out.println(emps[i]);
double salary=emps[i].earnings();
System.out.println("月工资为"+salary);
if (month==emps[i].getBirthday().getMonth()){
System.out.println("生日快乐,奖励100元");
}
}
}
}
package Day15.Abstruct.MyDate;
public class MyDate {
private int year;
private int month;
private int day;
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 getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public String toDateString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}
package Day15.Abstruct.MyDate;
public abstract class Employee {
private String name;
private int number;
public MyDate birthday;
public abstract double earnings();
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;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString() +
'}';
}
}
package Day15.Abstruct.MyDate;
/**
* (4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的
* 员工处理。该类包括:
* private成员变量wage和hour;
* 实现父类的抽象方法earnings(),该方法返回wage*hour值;
* toString()方法输出员工类型信息及员工的name,number,birthday。
*/
public class HourEmployee extends Employee{
private double wage ;//工资
private double hour;//小时
public HourEmployee(String name, int number, MyDate birthday ,int wage,int hour) {
super(name, number, birthday);
this.hour=hour;
this.wage=wage;
}
@Override
public double earnings() {
return wage*hour;
}
@Override
public String toString() {
return super.toString();
}
}
package Day15.Abstruct.MyDate;
public class SalaryEmployee extends Employee{
private int monthlySalary;//月工资
public SalaryEmployee(String name, int number, MyDate birthday ,int monthlySalary) {
super(name, number, birthday);
this.monthlySalary=monthlySalary;
}
@Override
public double earnings() {
return monthlySalary;
}
@Override
public String toString() {
return "SalaryEmployee{" +
"monthlySalary=" + monthlySalary + super.toString() +
'}';
}
}
3,接口(interface)
- 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
- 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都
支持USB连接。- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则 必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能" 的关系。
- 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守
3.1,接口的使用
1.接口使用interface来定义
2.Java中,接口和类是并列的两个结构
3.如何定义接口:定义接口中的成员
- 3.1 JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:
public static final
的.但是书写时,可以省略不写 - 抽象方法:
public abstract
的
- 全局常量:
- 3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
- 3.1 JDK7及以前:只能定义全局常量和抽象方法
-
- 接口中不能定义构造器的!意味着接口不可以实例化
-
- Java开发中,接口通过让类去实现(implements)的方式来使用.如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
-
- Java类可以实现多个接口 —>弥补了Java单继承性的局限性 格式:先写继承后写实现
class AA extends BB implements CC,DD,EE
- Java类可以实现多个接口 —>弥补了Java单继承性的局限性 格式:先写继承后写实现
-
- 接口与接口之间可以继承,而且可以多继承
-
- 接口的具体使用,体现多态性
-
- 接口,实际上可以看做是一种规范
package Day15.Interface;
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
Plane plane=new Plane();
plane.fly();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED=7900;//第一宇宙速度
public static final int MIN_SPEED=1;//省略了public static final
//抽象方法
public abstract void fly();
//省略了public abstract
public abstract void stop();
//接口中不能定义构造器,意味着接口不能实例化
// public Flyable(){
//
// }
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
abstract class Kite implements Flyable{
@Override
public void fly() {
}
}
class Bullet extends Object implements CC,Flyable,Attackable{
@Override
public void fly() {
}
@Override
public void stop() {
}
@Override
public void attack() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
总结:
- 定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
- 一个类可以实现多个接口,接口也可以继承其它接口。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实
例化。否则,仍为抽象类。 - 接口的主要用途就是被实现类实现。(面向接口编程)
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,
接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义
(JDK7.0及之前),而没有变量和方法的实现。
3.2,接口的应用举例
3.2.1 USB接口
package Day15.Interface.Usb;
public class USBTest {
public static void main(String[] args) {
Computer computer=new Computer();
//1,创建了接口的非匿名实现类的匿名对象
Flash flash=new Flash();
computer.transferData(flash);
//2。创建类接口的非匿名实现类的匿名对象
computer.transferData(new Printer());
//3。创建了接口的匿名实现类的非匿名对象
USB phone=new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
computer.transferData(phone);
//4。创建了接口的匿名实现类的匿名对象
computer.transferData(new USB() {
@Override
public void start() {
System.out.println("mp3开始工作");
}
@Override
public void stop() {
System.out.println("MP3停止工作");
}
});
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体的传输细节");
usb.stop();
}
}
interface USB{
//定义了长宽最大最小传输速度
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开始工作");
}
@Override
public void stop() {
System.out.println("U盘停止工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机停止工作");
}
}
3.2.2 代理模式(Proxy)
概述:
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其
他对象提供一种代理以控制对这个对象的访问。
- 应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有
100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
- 分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- JDK自带的动态代理,需要反射等知识
3.2.3 工厂模式(Factory)
解决的问题 实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
具体模式
0,无工厂模式
1,简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
2,工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
3,抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
简单工厂模式:调用者只要知道他要什么,从哪里拿,如何创建,不需要知道。分工,多出了一个专门生产 Car 的实现类对象的工厂类。把调用者与创建者分离。
小结:
简单工厂模式也叫静态工厂模式,就是工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的实例对象。
缺点:对于增加新产品,不修改代码的话,是无法扩展的。违反了开闭原则(对扩展开放;对修改封闭)。工厂方法模式为了避免简单工厂模式的缺点,不完全满足 OCP(对扩展开放,对修改关闭)。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立的模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
3.3 抽象类和接口区别的总结
NO. | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、(Jdk8.0默认方法、静态方法) |
3 | 使用 | 子类继承父类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
6 | 对象 | 都通过对象的多态性产生实例化对象 | 都通过对象的多态性产生实例化对象 |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
9 | 选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 | 同上 |
3.4 练习4
- 定义一个接口用来实现两个对象的比较。 interface CompareObject{ public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当 前对象大;负数代表当前对象小 }
- 定义一个Circle类,声明redius属性,提供getter和setter方法
- 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在 ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半 径大小。
- 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo 方法比较两个类的半径大小。
- 思 考 : 参 照 上 述 做 法 定 义 矩 形 类 Rectangle 和 ComparableRectangle 类 , 在 ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。
参考代码:
package Day15.Interface.Test4;
public interface CompareObject {
public int compareTo(Object o);//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
}
class Circle{
private double redius;
public Double getRedius() {
return redius;
}
public void setRedius(double redius) {
this.redius = redius;
}
public Circle(double redius) {
this.redius = redius;
}
public Circle() {
}
}
class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(double redius) {
super(redius);
}
@Override
public int compareTo(Object o) {
if (this==o){
return 0;
}
if (o instanceof ComparableCircle){
ComparableCircle c= (ComparableCircle) o;
// return (this.getRedius()-c.getRedius());
//正确的
// if (this.getRedius()>c.getRedius()){
// return 1;
// }else if (this.getRedius()<c.getRedius()){
// return -1;
// }else {
// return 0;
// }
//当属性radius声明为Double类型时,可以调用包装类的方法
return this.getRedius().compareTo(c.getRedius());
}else {
return 0;
}
}
}
class InterfaceTest{
public static void main(String[] args) {
ComparableCircle cc1=new ComparableCircle(4.1);
ComparableCircle cc2 = new ComparableCircle(5.0);
int flag = cc1.compareTo(cc2);
if(flag>0){
System.out.println("当前对象大");
}else if (flag==0){
System.out.println("两圆半径相等");
}else{
System.out.println("当前对象小");
}
}
}
3.5 Java 8中关于接口的改进
- 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
- 默认方法:默认方法使用 default关键字修饰。可以通过实现类对象来调用。 我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认 方法。
参考代码:
package Day15.Interface.Java8;
import java.util.Calendar;
public class SubClassTest {
public static void main(String[] args) {
SubClass s=new SubClass();
//1,静态方法无法被调用,只能通过接口来调用
CompareA.method1();
// s.method1();
//2,通过实现类的对象可以实现接口的默认方法
//如果实现类重写了接口中的默认方法,调用后仍然实现重写后的方法
s.method2();
//3,如果子类继承的父类的实现的接口中声明了同名同参数的方法
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法———>类优先原则
//4,如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法
//那么在实现类没有重写此方法的情况下,报错---》接口冲突
//若要避免冲突就必须要重写方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
@Override
public void method2() {
System.out.println("SubClass:上海");
}
@Override
public void method3(){
System.out.println("SuperClass:深圳");
}
public void MyMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类声明的
CompareA.super.method3();
CompareB.super.method3();
}
}
class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
package Day15.Interface.Java8;
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法14
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
package Day15.Interface.Java8;
public interface CompareB {
default void method3(){
System.out.println("CompareB:上海");
}
}
4,类的成员之五:内部类
引入:
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使+用内部类。
- 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
- Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
- Inner class的名字不能与包含它的外部类类名相同;
- 分类: 成员内部类(static成员内部类和非static成员内部类) 局部内部类(不谈修饰符)、匿名内部类
4.1,内部类介绍
内部类:类的第五个成员
- 定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
- 内部类的分类:
成员内部类(静态、非静态 ) vs 局部内部类(方法内、代码块内、构造器内) - 成员内部类的理解:
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
另一方面,作为一个类: - 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
- 成员内部类:
4.1 如何创建成员内部类的对象?(静态的,非静态的)
//创建静态的Dog内部类的实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
//创建非静态的Bird内部类的实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person(); Person.Bird bird = p.new Bird();
4.2 如何在成员内部类中调用外部类的结构?
class Person{
String name = "小明";
public void eat(){
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
//Person.this.eat();
}
}
}
4.2,局部内部类的使用
局部内部类的特点
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但
是前面冠以外部类的类名和$符号,以及数字编号。 - 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方
都不能使用该类。 - 局部内部类可以使用外部类的成员,包括私有的。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局
部变量的声明周期不同所致。 - 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
- 局部内部类不能使用static修饰,因此也不能包含静态成员
package Day15.InnerClass;
public class InnerClassTest1 {
//开发中很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口类的对象
public Comparable getComparable(){
//创建了一个实现了Comparable接口的类
// class MyComparable implements Comparable{
// @Override
// public int compareTo(Object o){
// return 0;
// }
// }
// return new MyComparable();
//方法2
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
5. 总结
今天主要学习了抽象类(abstract)、接口(interface)以及一种特殊的类——内部类。抽象类是不能具体示例化的类,它的存在更像是专门为了类的继承时,为子类专门提供方法。接口的存在更多的是为子类添加某种能力或用途,它的存在就是为了能够让子类进行实现。个人认为抽象类是定义了子类的本质,而接口是赋予了子类各种能力。而内部类主要是为了专门为其外部类所使用的而创建的类,它的存在大多是专门为其外部类使用而不被其他类所使用,比如人能开车是人专属的能力,是其他种类所不具备的,而开车就可以作为人的内部类。