◉ 抽象类
属于引用数据类型
抽象类:有 abstract 修饰,且必须有构造器(默认无参构造器)
抽象方法:有 abstract 修饰,且所属的类必须为抽象类,不能有方法体
— 定义:public abstract void test();
—— 特性:
抽象类既可以有抽象方法也可以有实例方法
甚至可以没有抽象方法
有得有失:
- 得:得到了拥有抽象方法的能力
- 失:失去了创建对象的能力(但依旧可以有构造器)
解析:
- 如果可以创建对象,那么将会调用抽象方法,但抽象方法没有方法体不能被调用
- 子类继承父类后,在调用构造器时必须先调用父类的构造器(默认调用无参构造器)所以父类必须有构造器,抽象类也一样
—— 注意点:
- 如果一个类继承了抽象类,那么它必须重写该抽象类的所有抽象方法
- 只要有一个抽象方法没有被重写,该类也必须成为抽象类(也说明了抽象类可以再被抽象类继承)
- 子类重写父类的抽象方法时,abstract 修饰要去掉,最好加上@Override
◉ 接口
也属于引用数据类型
编译后生成的依然是 .class 字节码文件
— 定义:public interface Interface1
—— 特性:
- 接口是一种更彻底的抽象,只能包含抽象方法,不能有方法体的出现
- 一个接口可以继承多个接口,用 extends
- 接口也可以被多实现,一个类可以同时实现多个接口,用 implement
如果继承的接口中有同名的抽象方法,那么会重叠在一起当作一个来处理,这也是接口可以多继承的原因。
—— 省略:
接口中的抽象方法都是公开的,作为规范来使用,使用其他修饰符就会报错
所以在接口中可以不写 public abstract ,会自动添加上的
接口中只能有常量,没有变量的存在
所以如果没有加上常量声明,会自动加上 public static final
如以下代码编译通过
public interface Interface1
{
double PI = 3.1415926;
void in1();
}
—— 注意:
实现接口时,所有实现的方法都必须是 public 修饰的
原因:接口中的方法默认为 public ,而重写方法时要求访问权限不能低于原方法,那么不低于 public 访问权限的就只有 public ,所以只能用 public 修饰。
public class Main implements Interface1
{
@Override
void in1()
{
}
}
—— 特别注意:
没有继承关系的接口可以互转,编译可以通过不会报错。
但运行阶段时,如果发现互转后两个接口真正所代表的没有继承关系,就会出现异常 ClassCastException
例子:
interface Inf1
{
void in1();
}
interface Inf2
{
void in2();
}
interface Inf3
{
void in3();
}
public class Test2 implements Inf1, Inf2
{
@Override
public void in1() {
System.out.println("in1");
}
@Override
public void in2() {
System.out.println("in2");
}
public static void main(String[] args) {
Inf1 inf1 = new Test2();
Inf2 inf2 = new Test2();
inf1.in1();
// 这个和多态一样,inf1 中并没有in2方法,所以即使 Test2 中有也会报错
// inf1.in2();
inf2.in2();
// inf2.in1(); 同上
// 接口互转 1
// 这里 Inf2 和 Inf1 并没有继承关系,但依旧可以互换不报错
Inf2 inf22 = (Inf2)inf1;
// inf1 转化成 Inf2 之后就可以调用 in2 方法了
// 这里运行时没有问题,因为 inf1 的类型为 Test2 ,是 Inf2 的子类
// 有继承关系,所以可以正常运行
inf22.in2();
// 接口互转2
Inf3 inf3 = new Inf3() {
@Override
public void in3() {
System.out.println("in3");
}
};
// 同上,没有继承关系但互转不报错
Inf2 inf222 = (Inf2)inf3;
// 这里就会出现异常了
// 因为 inf3 的类型为 Inf3 和 Inf2 没有继承关系,运行时出现异常
inf222.in2();
}
}
总结:
接口没有继承关系仍旧可以互转,编译时不会报错。但真正运行时,必须要求被转化的接口和转化后的接口有继承关系,否则出现 ClassCastException
异常
从上面例子知道:
inf1
的类型是 Test2 , inf22
的类型是 Inf2 ,而 Test2 又是 Inf2 的子类,有继承关系,所以 inf1
可以通过转化变成 inf22
,然后正常使用 Inf2 中的方法。
而inf3
的类型是 Inf3 , inf2
的类型是 Inf2 ,两者并没有继承关系,所以将 inf3
强转成 inf222
就会出现异常
优化:
跟多态一样,可以使用 instanceof 对转化进行判断,避免程序运行过程中突然中断
修改这一段
Inf2 inf222 = (Inf2)inf3;
inf222.in2();
if(inf3 instanceof Inf2)
{
Inf2 inf222 = (Inf2)inf3;
inf222.in2();
}
else
{
System.out.println("inf3 不能转化成 Inf2类型");
}
运行结果:inf3 不能转化成 Inf2类型
不会出现异常
—— 新增:
JDK1.8 之后
接口可以有 类方法 和 实例方法(必须用 public default 修饰)
如以下代码编译通过
public interface Interface1
{
// 实例方法
default void din1()
{
}
// 类方法 / 静态方法
static void din2()
{
}
}
◉ 内部类
在类里边定义类
而不是同一个 .java 文件的两个类,这两个类仅仅只是单独的两个类
作用: 提供了更安全的访问机制,内部类允许用 private 修饰,而类中的成员也可以用 private 修饰,更加安全
静态内部类
可以访问外部类的静态变量,不可以访问实例变量
继承静态内部类:直接 extends 外部类.内部类
实例内部类
特性:
- 不能有静态方法和静态成员,但可以有常量,使用 static final 修饰
- 可以访问外部类的静态成员,也可以访问实例成员
- 因为实例内部类属于对象而不属于类,当对象被new出来很多份之后,实例内部类也相应地会有很多份,而静态只要求有一份,所以不允许有静态方法和静态变量出现在实例内部类。所以静态的东西都应该放在外部类。
- 内部类和外部了有同名的变量,默认会访问内部类中的变量
那么如何访问外部类的变量?
解决:使用 外部类名称.this.变量名称 进行访问 - 在继承实例内部类时,不能直接用 super 去构造内部类,因为不知道该内部类是属于哪个外部类对象的,所以应该先提供一个外部类对象,再指定由该外部类对象去创建一个实例内部类对象。
public class Main extends Test.Inner
{
public Main(Test te)
{
te.super();
}
public static void main(String[] args)
{
Test te = new Test();
Main main = new Main(te);
}
}
public class Test
{
public class Inner
{
}
}
- 在创建实例内部类对象时,要先创建出外部类的实例,再由外部类去 new 一个实例内部类
Test te = new Test();
Test.Inner ti = te.new Inner();
局部内部类
存在于方法中的类,随方法的结束而结束
定义:
public void test()
{
class T1
{
}
}
匿名内部类
局部内部类的一员
很多时候我们要使用一个抽象类或者一个接口,都得单独写一个类去实现它们,这样就会显得很繁琐
匿名内部类就是用来对付这个问题的
抽象类和接口不能直接获取对象的原因是含有未实现的方法,也就是说只要实现了就可供以使用。
匿名内部类就是不创建新的类而直接实现抽象方法,以接口为例用法如下:
public class Main
{
public static void main(String[] args)
{
Interface1 inf = new Interface1()
{
@Override
public void in1() {
}
@Override
public void in2() {
}
};
}
}
public interface Interface1
{
void in1();
void in2();
}
◉ 总结
abstract 和 final 不能联合使用
abstract 和 final 在修饰类和方法的时候一定是互斥的
因为 abstract 的存在是为了派生子类,被子类继承和重写;
而 final 则强调该类或方法是最终的,不能再被继或者重写。
抽象类和接口的异同点
—— 相同点:
① 都不能被实例化
—— 不同点:
① 抽象类只能被单继承,而接口可以多继承
② 抽象类有构造器而接口没有
③ 抽象类可以有变量而接口中全部为常量
④ 接口需要用 implements 实现,而抽象类使用 extends 继承
public interface IceClean
{
void getUp();
void eat();
void study();
void releax();
void sleep();
}
(寒冰小澈)