接口
需求:需要飞到北京出差
飞机、鸟和超人不能归于一个类属,但是具备有相同的特性:会飞的。所以引入了一个新概念叫做接口,可以用于规范实现接口的类中必须实现接口中抽象方法。接口可以理解为一种契约
使用关键字interface定义接口
public interface 会飞的 {
public void 起飞();
public void 巡航飞行();
public void 降落();
}
接口不能直接使用,必须有对应的实现类
public class 飞机类 implements 会飞的 { //共性是通过实现接口来表示的
private String name; //名称,这是类属的属性,这里可以定义字节类的成员,和接口无关
//如果当前类不是抽象类,则必须实现接口中的所有抽象方法
@Override
public void 起飞() {
System.out.println("使劲的跑,一抬头就飞起来了");
}
@Override
public void 巡航飞行() {
System.out.println("使劲的烧油...");
}
@Override
public void 降落() {
System.out.println("我对准...");
}
}
通过接口定义变量,使用具体类的实例,进行调用
会飞的 obj = new 飞机类();
obj.起飞();
obj.巡航飞行();
obj.降落();
引入接口的目的在于隔离实现
public void 出差(飞机 obj){}
这种写法当前类和飞机是耦合的,如果需要坐超人出差,则必须修改源代码
public void 出差(会飞的 obj){}
当前类只是和接口耦合,任何实现了接口的对象都可以作为参数进行传入
什么是接口
在Java中不直接支持多继承,因为会出现调用的不确定性,所以Java将多继承机制进行改良,在Java中变成了多实现。一个类可以实现多个接口,一个接口可以继承多个接口
interface IA{}
interface IB{}
interface IC extends IA,IB{} 正确的,其中IC中的方法等于IA+IB
class A implements IA,IB{}
IA a=new A();
IB b=new A();
- 接口是一种特殊的抽象类,接口只能被abstract或者public修饰
- 没有构造器方法
public interface IA{
public IA(){} //语法报错
}
- 没有属性,只能定义常量
public class Test {
public static void main(String[] args) {
System.out.println(IA.name); //常量可以直接使用
System.out.println(B.name); //实现类也可以直接访问常量
}
}
interface IA {
String name = "yanjun";// 接口中只能定义常量,不能定义属性,默认限定词
public static final,自定义限定词不能冲突
}
class B implements IA{}
- 可以包含抽象方法,也可以没有抽象方法
interface IA1 {
}// 这个接口中没有包含任何抽象方法
class A1Impl implements IA1 {
}
接口中的方法只能被public、default、abstract、static修饰
- 一般情况下接口中只定义抽象方法
- 定义的方法默认为公开的抽象方法
interface IA2{
int ee();//默认public abstract int ee();
public void pp();//方法的限定词可以是public, abstract, default,
static,前面的public可以省略,如果不写默认public
public abstract void cc();//这里abstract可以省略,默认就是abstract
//在JDK1.8中引入特殊关键字default,允许提供默认实现
public default void dd(){ //这个方法的调用必须有实现类
System.out.println("dddd....");
}
//在JDK1.8中允许在接口中定义静态方法
public static void ff(){ //这个静态方法可以通过接口名称直接调用
IA2.ff()
System.out.println("ffffff");
}
}
抽象方法必须在实现类中提供实现
class A2Impl implements IA2{ //在实现类中必须实现接口中的所有抽象方法,否则当前
类只能是抽象类
public int ee() {
return 0;
}
public void pp() {
System.out.println("A2Impl....");
}
public void cc() {
System.out.println("A2Impl.....");
}
//dd方法由于在接口中有默认实现,所以可以不用重新定义,也允许重新定义
}
- 可以使用default关键字给抽象方法提供默认实现,有默认实现的方法在实现类中可以重新定义,也可以不重新定义
报错原因:Java中不支持类的多重继承,一个类只能有一个双亲类
接口允许多重继承
一个类在继承另一个类的同时,还可以实现多个接口
接口允许多重继承
interface IA1{
public void p1();
}
interface IA2{
public void p2();
}
interface IC extends IA1,IA2{
public void p3();
}//可以继承多个父接口,此时IC中实际有3个抽象方法
类是单根继承,不允许继承多个父类
允许一个类实现多个接口,但是每个接口的抽象方法都必须提供实现,否则是抽象类。提供的实现也可以是继承
public class A1{
public void p1(){}
}
public class A2 extends A1 implements IA1,IA2{
public void p2(){}
}
public abstract class A3 extends A1 implements IA1,IA2{}//因为p2方法没有实现
接口的出现避免了单继承的局限性,这样定义C接口则拥有A+B的所有定义,可以使用A和B接口以及父类D声明变量类型,直接new T。但是约束时,用谁声明变量编译器系统识别就是谁这种类型,也就意味只能调用识别类型中的方法,不能调用其他方法
interface A{
public void p1();
}
interface B{
public void p2();
}
interface C extends A,B{}
class D{
public void abc(){}
}
class T extends D implements C{
public void p1(){}
public void p2(){}
}
D d=new T();这里只能调用abc方法,如果使用其他方法则需要进行类型转换
A a=new T();这里只能调用A接口中声明的方法p1
B b=new T();这里只能调用B接口中声明的方法p2
声明接口的语法
访问修饰符 interface 接口名{ } 一般建议接口名称使用形容词
- 如果定义public接口,则规则和定义public类一致,要求接口名称和文件名称一致
- 外部的接口只能使用public、默认这两个范围限定词;如果定义内部接口则可以使用4大范围限定词
- 接口实际上提供了同一的操作界面(方法),如果是JDK1.8-版本则一个方法也不实现,等待某类或某几个类去实现它的方法【接口中的所有方法必须是抽象的】。如果使用的是JDK1.8+允许使用default在接口中定义默认实现,这个实现允许在实现类中重新定义覆盖
public class Test1 {
public static void main(String[] args) {
IA2 a2=new A2Impl();
a2.pp();
}
}
interface IA2 {
default void pp(){
System.out.println("this is pp....");
this.ff();
}
void ff();
}
class A2Impl implements IA2{
public void ff() {
System.out.println("this is ff...");
}
}
default默认方法实现使用的限制
public class Test1 {
public static void main(String[] args) {
IA2 a2 = new A2Impl();
a2.pp();
}
}
interface IA2 {
default void pp() {
System.out.println("this is No2 pp....");
}
}
interface IA3 {
default void pp(){
System.out.println("this is No3 pp");
}
}
class A2Impl implements IA2, IA3 {//如果IA2中有一个default实现的方法和IA3中
的方法一致,则必须在实现类中重新定义
public void pp() {
IA2.super.pp();//调用IA2接口中的pp方法的默认实现
IA3.super.pp();
}
}
- 接口中没有属性,只能定义常量,它提供一些常量,实现它的类可以共享这些常量
- 接口可以给出访问控制符,用public修饰的是公共接口,到处可见;如果定义接口没有范围限定词,则只能在同包中访问
接口中只能定义常量和抽象方法
- 接口中只能定义公共的常量,接口中的属性默认是public static final类型的,必须是public static final类型的
interface IA{
String name="yanjun";//这里实际上是常量定义,不是属性,它的限定词是public
static final String name="yanjun",必须在声明的同时进行赋值
} 在接口中不能定义静态块 static{}
接口中不能定义构造器
- 接口中只能定义公共的抽象方法, 只有在JDK1.8+中可以使用default关键字定义方法实现。接口中的方法默认是public abstract类型的,而且必须是public abstract类型的。只有在JDK1.8+中可以使用default关键字定义方法默认实现,同时允许在实现类中覆盖重新定义
- 接口不能被实例化,只能通过实现类所实现,但是可以用于声明变量的类型。
接口 变量名=new 实现接口类();
- 接口没有构造函数
- 接口中的抽象方法必须在非抽象子类中提供实现,这个实现可以是继承来的
interface IA2 {
public void pp();
}
class A2 {
public void pp() {
}
}
class B2 extends A2 implements IA2 { //这里的pp方法的实现是从父类中继承来的
}
一个类实现接口的语法
class 类名称 implements 接口名称 { }```
* 接口抽象方法的默认修饰符为public,在实现接口时必须用public关键字在方法头上说明
```java
interface IA2 {
void pp();
}
class B2 implements IA2 {
void pp() { //语法报错,因为IA2接口中的方法为public abstract
}
}
- 一个接口可以被多个子类实现。一个子类还可以实现多个接口
类实现接口
- 一个类在实现某接口的抽象方法时,必须使用完全相同的方法头public
- 如果一个类实现多接口,用逗号隔开
- 如果一个类实现了声明相同方法的两个接口,则被多个接口共用
public class Test1 {
public static void main(String[] args) {
IA2 a2=new B2(); a2.pp();
IA3 a3=new B2(); a3.pp();
}
}
interface IA2 {
void pp();
}
interface IA3{
void pp();
}
class B2 implements IA2,IA3 {
public void pp() {
System.out.println("this is B2.pp()");
}
}
- 类在实现接口时可以定义它自己的附加成员,这也是最常见的形式
public class Test1 {
public static void main(String[] args) {
IA2 a2=new B2();
a2.pp();//这里不能直接调用B2实现类中的其它方法,如果需要调用则应该进行强制
类型转换
((B2)a2).dd();
}
}
interface IA2 {
void pp();
}
class B2 implements IA2 {
public void pp() {
System.out.println("this is B2.pp()");
}
private int age=100;
public void pp(int k){
System.out.println("this is pp(int)");
}
public void dd(){
System.out.println("this is dd()");
}
}
- 如果一个类不完全实现接口的所有方法,必须把类定义成abstract类型的类,任何继承该类的子类必须实现该接口
接口中常量
- 可以为多个接口的实现类共享常量
public class Test1 {
public static void main(String[] args) {
System.out.println(IA2.PI);//可以直接使用
A3 a=new A3();
a.setR(4);
System.out.println(a.getArea());
}
}
interface IA2 {
double PI=3.1415;
}
class A3 implements IA2{ //在实现类中也可以直接使用
private double r;
public double getArea(){
return PI*r*r;
}
public void setR(double r) {
this.r = r;
}
}
- 只需在接口中定义并赋值,此后不能再修改
- 在类中说明是接口的实现类,就可以使用该接口的常量了
一个对象往往有多重身份
- Java中一个子类只能继承一个父类 (不能表示)
- java中一个类可以实现多个接口。(可以表示)
class 类名称 implements 接口1,接口2, … { }
- 通过继承和运行时多态的双重机制,可以定义一个被很多不同却相关的对象类型的运用的一致的接口
- 维持抽象接口,甚至不需要重新编译,就可以调用新类的实例
public void 出差(会飞的 obj){ obj.起飞(); ...}
调用的时候可以传入不能类型的对象
出差(new 飞机()); --- 出差(new 鸟());
接口的特殊特征
- 一个类只能有一个父类!
- 一个类可以实现多个接口!
- 一个接口可以继承多个接口
interface 接口名 extends 接口名称1 ,父接口名称2 , ... { }
接口的作用
- 统一访问
接口 obj=new 实现1(); 可以还有实现2,实现3等
obj只能调用接口中定义的方法
- 解耦 通过接口可以隔离具体实现
解耦就是 在使用者 和 实现者 之间没有关系。 无论实现者如何改变实现,对于使用者使用不会变化
接口和抽象类的异同点
- 相同点:都是不断向上抽取而来的
- 不同点:
抽象类需要被继承,而且只能单继承
接口需要被实现,而且可以多实现 - 抽象类中可以定义抽象方法和非抽象方法,子类继承后可以直接使用非抽象方法
- 接口中可以定义抽象方法和default方法,抽象方法必须由子类去提供实现;JDK1.8+中允许接口中的方法有默认实现,实现类中可以直接使用默认实现,允许覆盖定义
- 抽象类的继承是is a关系,在定义该体系的基本共性内容
- 接口的实现是like a关系,在定义体系额外功能
- 接口中只能定义常量,而且必须被初始化,抽象类中可以定义属性,允许在声明时直接初始化,也可以不初始化,同时允许定义常量
接口中的公共方法应该全部是抽象的,JDK1.8+版本中可以通过default关键字定义方法的默认实现,允许定义静态方法,JDK1.9开始可以定义私有方法;抽象类中可以有抽象方法也可以有普通方法
public class Test1
public static void main(String[] args) {
IA2.pp();
A2Impl.pp();
IA2 a2 = new A2Impl();
a2.pp();// 语法报错
}
}
interface IA2 {
public default void cc(){}
public static void pp() {
System.out.println("IA2 static....");
}
}
class A2Impl implements IA2 {
public static void pp() {
System.out.println("A2Impl static..");
}
}
在接口种定义调用规范,抽象类实现接口,提供公共功能部分,最终具体实现类实现特殊的方法
public class Test2 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp();
}
}
class Fa {
public static void pp() {
System.out.println("Fa static....");
}
}
class Son extends Fa {
public static void pp() {
System.out.println("Son static....");
}
}
在接口种定义调用规范,抽象类实现接口,提供公共功能部分,最终具体实现类实现特殊的方法
public class Test2 {
public static void main(String[] args) {
Fa ff = new Son();
ff.pp();
}
}
class Fa {
public static void pp() {
System.out.println("Fa static....");
}
}
class Son extends Fa {
public static void pp() {
System.out.println("Son static....");
}
}
public void pp();在接口中是抽象方法,但是如果public void pp(){}语法错误,在JDK1.8+版本中可以使用public default void pp(){}就合法了
如何使用接口
一般使用接口隔离具体实现,可以将类之间的相互依赖变为类对接口的依赖。例如出差类和会飞的东西是通过会飞的接口进行隔离,这样不管出差类需要修改或者会飞的东西需要修改,都不会相互影响
如果一组相关的类中有公共的方法和特殊的方法,可以使用抽象类,在抽象类中固化公共的方法【算法骨架】,而无需具体子类重复实现;但是在抽象类中无法实现的方法可以延迟到子类中再实现。例如排序器BubbleSorter,其中抽象类BubbleSorter固化了所使用的冒泡排序算法,而将无法实现的bigger比较算法延迟到BubbleSorter的子类PigSorter中实现,同时PigSorter中也不需要重新定义排序算法
最佳软件开发实践:先定义接口规范调用方法,在使用抽象类实现接口定义公共方法,最后再定义具体子类实现所有的方法
接口和抽象类的使用场景
从设计层面看,抽象类体现继承关系is a,它主要描述类的从属关系或者父子关系,抽象类和它的派生类之间是典型的IS-A关系,即子is a父。
interface可以多实现,而且不要求实现者和interface定义在概念本质上是一致的,仅仅是实现了
interface定义的契约而已。它主要描述的是类型间的行为合同,接口和它的实现类之间是典型的CANDO关系,即子can do父。
为什么接口需要默认方法
在接口添加默认方法不需要修改实现类,接口新增的默认方法在实现类中直接可用。
另外还要注意默认方法冲突问题。
内部类
一个类或者接口定义在另外一个类后者接口的内部
public class A1{//外部类
class B1{}//内部类
interface C1{}//内部接口
}
将一个类定义置入另一个类定义中,这就叫作“内部类”
- 内部类之外的类称为外部类
- 内部类的名称必须区别于它所在的外部类,和其它类之间没有要求
内部类的全名叫做[外部类名称$内部类名称]
public class A1 {
public class A1{} //语法报错
class B1{} 语法正确,默认编译的结果名称为A1$B1.class
}
class B1{}
- 内部类可以访问其外部类的所有变量和方法
public class A1 {//外部类的范围限定词只能是public或者默认package
private String name;
public class B1 { //内部类的范围限定词可以是4种
public void pp() {
System.out.println(name);
System.out.println(A1.this.name);//这里的A1.this用于表示A1类对
象
A1.this.name="ffff";
// System.out.println(this.name);报错的原因是this用于指代当前类
的对象,当前类B1中并没有属性name
pp();
A1.this.pp();
}
}
private void pp(){}
}
- 外部类不能直接访问内部类的实现细节
public class A1 {
private String name;
public class B1 {
private int age=99;
public void ff() {
System.out.println(name);
System.out.println(A1.this.name);
A1.this.name="ffff";
}
private void dd(){}
}
private void pp(){
B1 b=new B1(); //如果需要在外部类中访问内部类的实现细节则需要构建内部类
对象,然后通过内部类对象进行访问
b.ff();
System.out.println(b.age); //注意:可以直接访问内部类中的私有属性
b.dd();//私有方法仍旧可以直接调用
}
}
- 内部类比外部类多了private/protected/static三个修饰符,这三个修饰符不能用在外部类上
- 非静态内部类不能拥有静态成员
protected class B1 {
private int age = 99;
{ }//允许包含非静态代码块
private static String password="123456";//非静态内部类中不能包含静态属
性
static{ }//非静态内部类中不允许包含静态代码块
public B1() {//允许定义构造器和析构器方法
}
public static void hh(){} //非静态内部类中不允许包含静态方法
内部类的作用
内部类提供更好的封装
内部类可以直接访问外部类的私有成员
外部类不能直接访问内部类的成员,需要构建内部类对象才能访问
匿名内部类适合用于创建仅仅使用一次使用的类
内部类相关的设计
-
分析事物时发现该事物描述还有事物,而且这个事物还在访问被描述事物的内容
例如牛和牛腿如果一个事物离开另外一个事物后则没有任何意义,这种情况下建议使用内部类,不允许其他类访问
-
内部类能直接访问外部类中成员
-
是因为内部类持有了外部类的引用,即外部类名.this
内部类分类
在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
内部类实际拥有外部类的一个引用,在构造函数中将外部类的引用传递进来。
非静态内部类
基础语法
public class A1{
protected class B1{}//静态内部类是protected static class B1{}
}
非静态内部类的特点:
和其他类一样,它只是定义在外部类中的另一个完整的类结构
- 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
- 可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量。
- 可以使用abstract修饰,因此它也可以被其他类继承
- 可以使用final修饰,表示不能被继承
- 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
- 外部类只允许public或缺省的
还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的
在外部类的静态成员中不可以使用非静态内部类哦
- 就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样
在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象
- 因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的
this对象
在创建非静态内部类对象时,一定要先创建起相应的外部类对象
public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc = b.new C1(); //内部类的写法为[外部类名.内部类名],否则需要
import com.yan.B1.C1;则可以直接使用C1类。至于C1类是否可见取决于C1类的范围限定词
System.out.println(cc);
}
}
class B1 {
public class C1 {
}
}
- 在任何非静态内部类中,都不能有静态数据、静态方法或者又一个静态内部类
语法错误
public class A1{
public class B1{
public static String name="yan";//语法错误,除非B1类是静态内部类
static class B1{}
static{}//语法错误
public static void pp(){}//语法错误
public static class C1{}//语法错误
}
}
注意内部类的可见性范围限制
访问方法:直接访问外部类中内部类中的成员
class B1 {
private String pwd;
private void dd(){}
protected class C1 {
private String name;
private void pp(){
System.out.println(pwd);//内部类可以直接访问外部类中的私有成
员
//另外的写法 B1.this.pwd
dd();
//另外的写法 B1.this.dd();
}
}
public void ee(){
//如果需要访问内部类中的成员,则必须首先创建内部类对象
C1 cc=new C1();
//通过构建的内部类对象则可以访问内部类中的私有成员
cc.name="abc";
cc.pp();
}
}
- 外部类.内部类 in=new 外部类().new 内部类();
public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc=b.new C1();//内部类是否可以访问到,取决于内部类上的范围限
定词
}
}
class B1 {
class C1{}
}
- in.内部类方法();
public class A1 {
public static void main(String[] args) {
B1 b = new B1();
B1.C1 cc=b.new C1();
cc.pp(); //是否可以访问pp方法取决于pp方法的范围限定词
}
}
class B1 {
class C1{
public void pp(){}
}
}
- 注意内部类的可见性范围限制
class B1 {
private class C1{//注意这个C1类只能在B1类中进行使用,其它位置不可见
public void pp(){}
}
}
问题
public class A1 {
public A1(){
System.out.println("aaaaa");
}
public static void main(String[] args) {
new A1();//创建外部类对象和内部类无关,创建外部类对象并不会自动创建内
部类对象
}
class C1 {
public C1() {
System.out.println("dddd");
}
}
}
静态内部类
基础语法:
public class A1{
public static class B1{}//静态内部类
}
静态内部类的特点:
和其他类一样,它只是定义在外部类中的另一个完整的类结构
- 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
- 可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员
- 可以使用abstract修饰,因此它也可以被其他类继承
- 可以使用final修饰,表示不能被继承
- 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
- 外部类只允许public或缺省的
只可以在静态内部类中使用外部类的静态成员
- 在静态内部类中不能使用外部类的非静态成员哦
在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象
如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别
静态内部类中,可以有静态数据、静态方法或者又一个静态内部类
class B1 {
static class C1 {// 在静态内部类中才可以定义静态成员
private static int age = 123;
private String pwd="123456";//同时允许定义非静态成员
static {
System.out.println("c1 static...");
}
public static void pp() {
System.out.println("c1 static pp...");
}
public void ff(){//同时允许定义非静态成员
}
static class D1{}
class E1{}
}
}
如何使用静态内部类
public class A1 {
public static void main(String[] args) {
B1.C1 cc = new B1.C1();//不需要构建外部类对象,就可以直接创建静态内部类对
象,但是是否可见取决于范围限定词
B1.C1.pp();//可以直接访问静态内部类中的静态成员,但是是否可见取决于范围限定
词
cc.ff();
}
}
问题
public class A1 {
public static void main(String[] args) {
B1.C1 cc = new B1.C1();
}
}
class B1 {
private B1() {
System.out.println("bbbbbb");
}
static class C1 {// 在静态内部类中才可以定义静态成员
public C1() {
System.out.println("cccccc");
}
static {
System.out.println("c1 static...");
}
输出只有
c1 static... 类加载完成后自动执行
cccccc 构建静态内部类对象和外部类无关,没有要求必须先构建外部类对象
- 静态内部类中,也可以有非静态数据、非静态方法或者又一个非静态内部类
- 静态内部类中,不能访问外部类的非静态成员,这是由Java语法中【静态方法不能直接访问非静态成员】所限定
class B1 {
private static String name1 = "yan1";
private double salary = 123.456;
static class C1 {
private static int age = 123;
private String pwd = "abc";
public static void pp() {
System.out.println(age);// 静态方法只能访问静态成员
// System.out.println(this.age);在靜態方法中不允許使用this或者
super
// System.out.println(pwd); 语法报错
System.out.println(name1);// 静态内部类中可以直接访问外部类中的静
态成员
dd();
// System.out.println(salary);语法报错,因为静态方法只能访问静态成
员
}
public void fff() {
System.out.println(this.age);
System.out.println(pwd);//当前类中的非静态成员
System.out.println(name1);// 不允许使用B1.this或者this.进行访
问,不允许使用B1.this是因为构建C1类对象时没有要求必须构建外部类对象
// System.out.println(salary);报错的原因是构建构建C1类对象时没有
要求必须构建外部类对象
dd();
// ee();z直接访问ee方法报错,构建构建C1类对象时没有要求必须构建外部类
对象
}
}
private void ee() {
}
private static void dd() {
}
}
问题1:
class B1 {
static class C1 {
}
}
构建内部类对象的方法:
B1.C1 cc = new B1.C1();
以下创建方法错误:
B1 b = new B1();
B1.C1 c = b.new C1();
问题2:
public class A1 {
public static void main(String[] args) {
B1.C1 bcc=new B1.C1();
}
}
class B1 {
private static D1 dd=new D1();
static {
System.out.println("B1 static....");
}
static class C1 {
private static E1 dd=new E1();
static {
System.out.println("C1 static...");
}
}
}
这里执行会发现B1类的静态代码块和静态属性并没有执行处理,所以要理解外部类实际上是内部类
的一个名空间。加载内部类时实际上并没有加载外部类----重点
局部内部类
可以将内部类定义在一个方法或者一个代码块内
public class A1 {
private int age = 123;
public static void main(String[] args) {
A1 aa = new A1();
}
public void pp() {
Date birth = new Date();
// B1 bb=new B1();语法错误,要求局部内部类必须先定义后使用
class B1 { // 局部内部类,只能在所在的{}范围中使用,具备内部类的范围限定词和临
时变量一致,只能添加final或者abstract
public void ff() {
System.out.println(age);
A1.this.age = 555;// 可以直接访问外部类中的成员
System.out.println(birth);
// 语法报错,在局部内部类中使用外部的临时变量,则外部临时变量必须是
final的,只是final可以省略
// birth = new Date();针对引用类型修改地址是不允许,但是可以修改属性
System.out.println("B1...ff()");
}
}
B1 bb = new B1();
}
}
局部内部类的特点:
和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构
- 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无
关 - 可以在局部内部类中声明属性、方法、构造器等结构,但不包括静态成员,除非是从父类继承的或静态常量
- 可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承
- 可以使用final修饰,表示不能被继承
- 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
* 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
和成员内部类不同的是,它前面不能有权限修饰符等
局部内部类如同局部变量一样,有作用域
局部内部类中是否能访问外部类的静态还是非静态的成员,取决于所在的方法
局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量
- JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final
问题:
public class A1 {
private int age = 123;
public static void main(String[] args) {
A1 aa = new A1();
}
public void pp() {
static class B1 {//局部内部类不能是static
static int age = 999;//不允许包含静态成员
static{}
public static void ff(){}
}
}
}
注意:局部内部类需要先定义后使用,不能是先使用后定义
匿名内部类
匿名内部类就是内部类的简写格式
public class A1{
public void pp(){
class B1 extends C1{}
}
}
//简化写法
public class A1{
public void pp(){
new C1(){};//原始命名类的写法class B1 extends C1{} new B1();
}
}
public class A1 {
public void pp(){
new Object(){
public void ff(){ //ff方法在父类中没有定义,则只能直接调用
System.out.println("anon inner class ... ff");
}
}.ff();
}
public static void main(String[] args) {
A1 a1=new A1();
a1.pp();
}
}
下面写法使用较多
public class A1 {
public void pp(){
Object obj=new Object(){
@Override//覆盖父类中定义的方法
public String toString() {
return ("anon inner class ... ff");
}
public void dd(){} //无法调用
};
System.out.println(obj.toString());//调用匿名内部类中覆盖定义的方法
}
}
匿名内部类的前提是必须继承或者实现一个外部类或者接口
new interfacename(){......};
new superclassname(){......};
如果父类中没有无参构造器,则()中应该有对应的参数
public class A1 {
public void pp(){
new B1(20){ //因为B1类中没有无参构造器,必须直接传入参数
};
}
}
class B1{
private int age;
public B1(int age){
this.age=age;
}
}
- 匿名内部类由于没有名字,所以它没有构造函数
- 如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数
- 不能定义静态成员
匿名内部类的使用场景
当方法参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数进行传递
匿名内部类的使用限制
- 匿名内部类不能是抽象的
- 匿名内部类不能定义构造器,默认和父类相似的构造器
- JDK1.8-要求给局部内部类、匿名内部类访问的局部变量必须使用final修饰,从JDK1.8开始这个现实被取消了,但是默认是final的(不能修改)
public void pp() {
final Date birth = new Date();
class C1 {
public void ff() {
System.out.println(birth);
birth.setYear(2000);// 3900
// birth=new Date(); 语法报错
System.out.println("modify year:" + birth);
}
}
new C1().ff();
}
int等8种简单类型/Integer8种简单类型的包装类/String的使用中应该注意
内部类的使用场景和好处
- 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏
public class 牛{
private class 牛腿{}
}
- 方便编写事件驱动程序
btn.addActionListener(new ActionListener() { //添加按钮对应的事件处理
public void actionPerformed(ActionEvent e) {
showNewWindow(mainWin);
}
});
- 方便编写线程代码。
练习题
第1题考核知识点:属性与多态无关
package com.yan.test01;
public class Test01 {
public static void main(String[] args) {
A a = new B();
System.out.println(a.num);
System.out.println(((B)a).num);
System.out.println(((A)((B)a)).num);
System.out.println("-------------------");
B b = new B();
System.out.println(b.num);
System.out.println(((A)b).num);
System.out.println(((B)((A)b)).num);
}
}
class A{
int num = 1;
}
class B extends A{
int num = 2;
}
第2题考核知识点:实例初始化方法,属性与多态无关
package com.yan.test02;
public class Test02 {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}
class Father{
int x = 10;
public Father(){
this.print();
x = 20;
}
public void print(){
System.out.println("Father.x = " + x);
}
}
class Son extends Father{
int x = 30;
public Son(){
this.print();
x = 40;
}
public void print(){
System.out.println("Son.x = " + x);
}
}
第3题考核知识点:多态,重写,实例初始化过程
package com.yan.test03;
public class Test03 {
public static void main(String[] args) {
Base b1 = new Base();
Base b2 = new Sub();
}
}
class Base {
Base() {
method(100);
}
public void method(int i) {
System.out.println("base : " + i);
}
}
class Sub extends Base {
Sub() {
super.method(70);
}
public void method(int j) {
System.out.println("sub : " + j);
}
}
第4题考核知识点:多态、重载、重写
public class Test04 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("(1)" + a1.show(b));
System.out.println("(2)" + a2.show(d));
System.out.println("(3)" + b.show(c));
System.out.println("(4)" + b.show(d));
}
}
class A{
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return "A and A";
}
}
class B extends A{
public String show(B obj){
return "B and B";
}
public String show(A obj){
return "B and A";
}
}
class C extends B{
}
class D extends B{
}