二、接口的默认方法的冲突问题
1、一个实现类同时实现多个接口,当多个接口中包含签名相同的默认方法,方法体不同
方法的签名:【修饰符】 返回值类型 方法名(【形参列表】)
解决方案:
(1)保留其中一个接口的实现
(2)选择自定义,即自己重写
2、当一个实现类继承了父类又实现了接口,当父类中有和接口中的默认方法签名相同的方法时,方法体不同
解决方案:
(1)默认:编译器是保留的是父类的
(2)也可以选择保留其中一个接口的
(3)也可以完全自己重写
public class DefaultMethod {
public static void main(String[] args) {
C c = new C();
c.method();
}
}
interface A{
default void method(){
System.out.println("a");
}
}
interface B{
default void method(){
System.out.println("b");
}
}
class Father{
public void method(){
System.out.println("father");
}
}
//一个类可以同时实现多个接口
//可以把父类理解为亲生父亲,把接口理解为干爹
//亲生父亲是更亲密关系,所以默认保留的是父类的
class C extends Father implem
类与类,接口与接口,类与接口的关系:
(1)类与类:单继承的关系
(2)接口与接口:多继承的关系
(3)类与接口之间:一个类可以同时实现多个接口;
接口不能去继承类;
(4)类可以同时继承父类,又实现接口:继承在前,实现在后
向上转型与向下转型:
(1)向上类型转换:
当把子类类型的对象/变量a赋值给父类/父接口的变量b时,自动完成。
无风险: a变量类型 > b变量类型
(2)向下类型转换:
当把父类/父接口/兄弟类型的变量a赋值给子类/实现类/兄弟类型变量b时,强制完成。
有风险, a变量类型 <或≈ b类型 这里<是继承或实现关系,≈是指无继承或实现的同级关系。
编译时:理论上只要a变量类型 <或≈ b类型就可以编译通过,但是String、Integer等类型校验比较严格。
运行时:只有a变量中实际对象的类型 <或就是 b类型才可以成功,否则报ClassCastException异常
注意:
向上类型转换与向下类型转换都是针对编译时类型,
实际上对象的运行时类型并没有“转换”。
类型转换的本质没有变。原来new的是什么类型,就还是什么类型。
public class ClassCast {
public static void main(String[] args) {
SuperMan man = new SuperMan();
//自动类型转换,向上类型转换
//类型转换,其实可以理解为,看成是什么类型,不是真的是这个类型
Object o = man;
Animal a = man;
Person p = man;
Swimming s = man;
Flyable f= man;
//成功的向下转型
//o变量的编译时类型是Object类型,Object是SuperMan的父类,所以称为向下转型
//因为o变量中实际存储的是SuperMan类型,SuperMan类型是=SuperMan,该转型安全
SuperMan s1 = (SuperMan) o;
//p变量的编译时类型是Person类型,Person是SuperMan的父类,所以称为向下转型
//因为p变量中实际存储的是SuperMan类型,SuperMan类型是=SuperMan,该转型安全
SuperMan s2 = (SuperMan) p;
//p变量的编译时类型是Person类型,Swimming和Person没有继承也没有实现关系,可以称为同级关系,也需要强制类型转换,
//也归为向下转型
//因为p变量中实际存储的是SuperMan类型,SuperMan类型是=SuperMan,该转型安全
Swimming s3 = (Swimming) p;
//o编译时类是Object,Object是Person的父类,属于向下转型
//是否安全?
//o变量中实际存储的是SuperMan类型,SuperMan类型< Person类型,所以是安全的
Person p3 = (Person) o;
//p变量的编译时类型是Person类型,Swimming和Person是同级关系,看成向下转型
//是否安全?
//p变量中实际存储的是SuperMan类型,SuperMan<Swimming,是实现关系,安全
Swimming s4 = (Swimming) p;
//编译时,s是Swimming类型,Flyable和Swimming是同级关系,我们把它归到向下转型
//是否安全?
//s变量中实际存储的是SuperMan类型,SuperMan<Flyable,是实现关系,安全
Flyable f3 = (Flyable) s;
System.out.println("----------------------------------------");
Person person = new Person();
//编译时,Person和Swimming同级关系,看成向下转型
//是否安全?
//person中实际类型是Person类型,person 不是Swimming类型,也不小于Swimming类型,所以是失败的
Swimming s5 = (Swimming) person;
// String str = (String)person;//编译不通过
}
}
interface Flyable{
void fly();
}
interface Swimming{
void swim();
}
abstract class Animal{
}
class Person extends Animal{
public void walk(){}
}
//看SuperMan的UML类关系图快捷键:Ctrl + Alt + U
class SuperMan extends Person implements Flyable,Swimming{
@Override
public void fly() {
System.out.println("上天");
}
@Override
public void swim() {
System.out.println("入水");
}
@Override
public void walk() {
System.out.println("入地");
}
}
三、常用接口
1、java.lang.Comparable接口:自然比较接口,优先考虑
抽象方法:int compareTo(Object obj)
2、java.util.Comparator接口:定制比较接口,后补考虑
(1)为什么有了Comparable接口还要Comparator接口?
因为Comparable接口不能解决所有问题?
A:员工类:编号、姓名、薪资、年龄…
有一个数组,里面有5个员工对象,
①财务说:把这个5个员工按照薪资排序
②人事说:把这个5个员工按照年龄排序
③咨询说:把这个5个员工按照姓名排序
三个需求同时存在,那么只在Employee类中实现compareTo方法不够用。
我们需要Comparator接口帮我们定制其他的需求。
B:使用第三方的类,我们只有它的.class文件,或者甚至只有它的对象,
但是它的这个对象的类型没有实现Comparable接口,而我们在程序中需要对它们的对象进行排序。
此时我们无法去修改这个类型,让他实现Comparable接口。
所以此时我们仍然需要Comparator接口帮我们定制排序规则。
(2)Comparator接口有抽象方法:
int compare(Object o1, Object o2)
包装类:Double等里面有compare方法
Double类型: static int compare(double d1, double d2) 可以用于比较两个double值,
如果d1 > d2,返回正整数,
如果d1 < d2,返回负整数,
如果d1 = d2,返回值0,
public class Comparator {
public static void main(String[] args) {
Employee[] arr = {
new Employee(1,"张三",23,10000),
new Employee(2,"李四",21,15000),
new Employee(3,"王五",25,13000),
new Employee(4,"赵六",22,90000),
new Employee(5,"麻子",27,8000)
};
/* Employee[] arr = {
new Employee(1,"Alice",23,10000),
new Employee(2,"Rose",21,15000),
new Employee(3,"Lily",25,13000),
new Employee(4,"Lucy",22,90000),
new Employee(5,"Kate",27,8000)
};*/
//财务是按照薪资排序
//创建比较器对象,用比较器对象帮我们比较两个员工对象
SalaryComparator sc = new SalaryComparator();
MyArrays2.sort(arr,sc);
//遍历结果
MyArrays2.print(arr);
System.out.println("---------------------------------------");
//人事想要按照年龄
//创建年类比较器对象
AgeComparator ac = new AgeComparator();
MyArrays2.sort(arr, ac);
//遍历结果
MyArrays2.print(arr);
System.out.println("-------------------------------------");
// ③咨询说:把这个5个员工按照姓名排序,按照英文字母顺序排序
//创建按照名字比较的比较器对象
NameComparator nc = new NameComparator();
MyArrays2.sort(arr,nc);
//遍历结果
MyArrays2.print(arr);
System.out.println("-------------------------------------");
// ③咨询说:把这个5个员工按照姓名排序,按照拼音顺序比较
//创建按照名字比较的比较器对象
PinYinComparator pc = new PinYinComparator();
MyArrays2.sort(arr, pc);
//遍历结果
MyArrays2.print(arr);
}
}
根父类:
java.lang.Object类的所有方法都会继承到所有的引用数据类型中,我们需要了解它的11个方法的作用,使用规则。
那天学习了5个(toString,finalize,getClass,hashCode,equals)
今天再学习一个:
protected Object clone()throws CloneNotSupportedException
protected可见性范围:本类(Object内部),本包(java.lang),其他包的子类(Object的子类中)
说明: CloneNotSupportedException - 如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出此异常,
以指示无法复制某个实例。
换句话说,你需要支持Cloneable 接口,即实现Cloneable 接口。
Cloneable 接口没有抽象方法,因为它的功能由Object类的clone()已经实现了,我们所有类型都有这个方法,所以你不用重写了。
重写的要求:
(1)方法名:必须相同
(2)形参列表:必须相同
(3)返回值类型:
基本数据类型和void:必须相同
引用数据类型:<=
(4)权限修饰符:>=
(5)其他修饰符:不能是static, final, private等
public class Cloneable {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu = new Student(1,"张三");
Object cloneObj = stu.clone();//因为继承的Object的clone方法的修饰符是protected,意味着只能从Student类的内部去调用
/*
除非,我对这个方法进行重写,并且把protected修改为public
*/
// 此时cloneObj和stu两个对象是复制品的关系
System.out.println(stu);
System.out.println(cloneObj);
System.out.println(stu == cloneObj);//比较地址 不同 false
System.out.println(stu.equals(cloneObj));//返回true还是false,要看是否重写了
}
}
class Student implements Cloneable
类的结构:
【修饰符】 class 类名 【extends 父类】 【implements 接口们】{
//(1)成员变量
【修饰符】 数据类型 成员变量名;
//(2)构造器
【修饰符】 类名(){
}
【修饰符】 类名(形参列表){
}
//(3)成员方法
【修饰符】 返回值类型 方法名(【形参列表】){
}
//(4)代码块
{
//非静态代码块
}
static{
//静态代码块
}
//(5)成员内部类
}
一、内部类
1、什么是内部类?
声明在其他类的里面的类称为内部类。
2、内部类有两大类/四小类?
根据内部类的声明的位置不同分为两大类:
(1)成员内部类
又根据是否有static修饰,再细分为两类:
A:静态内部类
B:非静态成员内部类
(2)局部内部类
又根据是否有名字来分:
A:有名字的局部内部类(非常少见)
B:匿名内部类
和变量的分类有点像:成员变量(静态变量和实例变量)和局部变量
(一)静态内部类
1、语法结构
【修饰符】 class 外部类名 【extends 父类】 【implements 接口们】{
【其他修饰符】 static class 静态内部类名 【extends 父类】 【implements 接口们】{
}
}
2、特点
(1)静态内部类也是一个类
==>①可以有自己的名字、父类、父接口
②可以有自己的类结构:成员变量、构造器、代码块、成员方法、内部类
③有自己的字节码文件
每一个类都有自己的字节码文件,不管是外部类还是内部类
(2)静态内部类也可以创建对象
(3)修饰符:
外部类的修饰符:public,缺省,final,abstract
静态内部类的修饰符:public,protected,缺省,private,final,abstract,static
3、如何使用它
(1)在外部类中使用:正常使用
(2)在外部类的外面的其他类中:
只要在静态内部类的前面冠以外部类名.即可正常使用。
4、如何在静态内部类中使用外部类的成员
只有一种情况不能使用:在静态内部类中,不能直接使用外部类的非静态的成员
public class StaticInner {
public static void main(String[] args) {
//调用Inner的inMethod()和inFun()
Outer.Inner.inMethod();
Outer.Inner in = new Outer.Inner();
in.inFun();
//此时使用import语句,简化了类名
Inner in2 = new Inner();
in.inFun();
}
}
class Outer{
private int a;
private static int b;
static class Inner {
public static void inMethod(){
System.out.println("静态内部类的静态方法");
// System.out.println("a = " + a);//报错,Inner是静态的,而a是非静态的
System.out.println("b = " + b);
}
public void inFun(){
System.out.println("静态内部类的静态方法");
}
}
public void test(){
//调用Inner的inMethod()和inFun()
Inner.inMethod();
Inner in = new Inner();
in.inFun();
}
public static void outMethod(){
//调用Inner的inMethod()和inFun()
Inner.inMethod();
Inner in = new Inner();
in.inFun();
}
}