当编写一个类时,经常会为该类定义一些方法,这些方法用以描述该类的行为方式,那么这些方法都有具体的方法体。但在某些状况下,某个父类只是知道其子类应该包含怎样的方法,但没法准确地知道这些子类如何实现这些方法。使用抽象方法便可知足该要求:抽象方法是只有方法签名,没有方法实现的方法java
抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里能够没有抽象方法算法
抽象方法和抽象类的规则以下:编程
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体设计模式
抽象类不能被实例化,没法使用new关键字来调用抽象类的构造器建立抽象类的实例。即便抽象类里不包含抽象方法,这个抽象类也不能建立实例数组
抽象类能够包含成员变量、方法(普通方法和抽象方法)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于建立实例,主要是用于被其子类调用app
含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有彻底实现父类包含的抽象方法;或实现了一个接口,但没有彻底实现接口包含的抽象方法三种状况)只能被定义成抽象类this
抽象类与空方法体的方法:public abstract void test(); public void test(){};设计
抽象类不能用于建立实例,只能看成父类被其余子类继承code
当使用abstract修饰类时,代表这个类只能被继承;当使用abstract修饰方法时,代表这个方法必须由子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。所以final和abstract永远不能同时使用对象
abstract不能用于修饰成员变量,不能用于修饰局部变量,即没有抽象变量、没有抽象成员变量等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器
当使用static修饰一个方法时,代表这个方法属于该类自己,即经过类就可调用该方法,但若是该方法被定义成抽象方法,则将致使经过该类来调用该方法时出现错误(调用了一个没有方法体的方法确定会引发错误)。所以static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法。但static和abstract能够同时修饰内部类
abstract关键字修饰的方法必须被其子类重写才有意义,不然这个方法将永远不会有方法体,所以abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法
抽象类体现的就是一种模板模式的设计,抽象类做为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类整体上会大体保留抽象类的行为方式。
模板模式在面向对象的软件中很经常使用,其原理简单,实现也很简单。下面是使用模板模式的一些简单规则:
抽象父类能够只定义须要使用的某些方法,把不能实现的部分抽象成抽象方法,留给其子类去实现
父类中可能包含须要调用其余系列方法的方法,这些被调用方法既能够由父类实现,也能够由其子类实现。父类里提供的方法只是定义了一个通用算法,其实现也许并不彻底由自身实现,而必须依赖于其子类的辅助
抽象类是从多个类中抽象出来的模板,若是将这种抽象进行得更完全,则能够提炼出一种更加特殊的“抽象类”——接口(interface),接口里不能包含普通方法,接口里的全部方法都是抽象方法。Java8对接口进行了改进,容许在接口中定义默认方法,默认方法能够提供方法实现
接口定义的是多个类共同的公共行为规范,这些行为是与外部交流的通道,这意味着接口里一般是定义一组公用方法。
[修饰符] interface 接口名 extends 父接口1,父接口2... { 零个到多个常量定义... 零个到多个抽象方法定义... 零个到多个内部类、接口、枚举定义... 零个到多个默认方法或类方法定义... }
修饰符能够是public或者省略,若是省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才能够访问该接口
接口名应与类名采用相同的命名规则,即若是仅从语法角度来看,接口名只要是合法的标识符便可;若是要遵照Java可读性规范,则接口名应由多个有意义的单词连缀而成,每一个单词首字母大写,单词与单词之间无须任何分隔符。接口名一般可以使用形容词。
一个接口能够有多个直接父接口,但接口只能继承接口,不能继承类。
因为接口定义的是一种规范,所以接口里不能包含构造器和初始化块定义。接口里能够包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)定义
接口里定义的是多个类共同的公共行为规范,所以接口里的全部成员,包括常量、方法、内部类和内部枚举都是public访问权限。定义接口成员时,能够省略访问控制修饰符,若是指定访问控制修饰符,则只能使用public访问控制修饰符。
对于接口里定义的静态常量而言,它们是接口相关的,所以系统会自动为这些成员变量增长static和final两个修饰符。也就是说,在接口中定义成员变量时,无论是否使用public static final修饰符,接口里的成员变量老是使用这三个修饰符来修饰。并且接口里没有构造器和初始化块,所以接口里定义的成员变量只能在定义时指定默认值
接口里定义的内部类、内部接口、内部枚举默认都采用public static两个修饰符,无论定义时是否指定这两个修饰符,系统都会自动使用public static对它们进行修饰
接口里定义的方法只能是抽象方法、类方法或默认方法,所以若是不是定义默认方法,系统将自动为普通方法增长abstract修饰符;定义接口里的普通方法时无论是否使用public abstract修饰符,接口里的普通方法老是public abstract来修饰。接口里的普通方法不能有方法实现(方法体);但类方法、默认(default)方法都必须有方法实现(方法体)
public interface Output { //接口里定义的成员变量只能是常量 int MAX_CACHE_LINE = 50; //接口里定义的普通方法只能是public的抽象方法 void out(); void getData(String msg); //在接口里定义默认方法,须要使用default修饰 default void print(String... msgs) { for (String msg : msgs) { System.out.println(msg); } } //在接口中定义默认方法,须要使用default修饰 default void test() { System.out.println("默认的test()方法"); } //在接口里定义类方法,须要使用static修饰 static String staticTest() { return "接口里的类方法"; } }
Java8容许在接口中定义类方法,类方法必须使用static修饰,该方法不能使用default修饰,不管程序是否指定,类方法老是使用public修饰——若是开发者没有指定public,系统会自动为类方法添加public修饰符。类方法能够直接使用接口来调用。
接口的继承和类继承不同,接口彻底支持多继承,即一个接口能够有多个直接父接口。和类继承类似,子接口扩展某个父接口,将会得到父接口里定义的全部抽象方法、常量。
一个接口继承多个父接口时,多个父接口排在extends关键字以后,多个父接口之间以英文逗号(,)隔开。
接口不能用于建立实例,但接口能够用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象。除此以外,接口的主要用途就是被实现类实现。
定义变量,也可用于进行强制类型转换
调用接口中定义的常量
被其余类实现
一个类能够实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字。
[修饰符] class 类名 extends 父类 implements 接口1,接口2 { 类体部分 }
一个类实现了一个或多个接口以后,这个类必须彻底实现这些接口里所定义的所有抽象方法(也就是重写这些抽象方法);不然,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
一个类实现某个接口时,该类将会得到接口中定义的常量(成员变量)、方法等,所以能够把实现接口理解为一种特殊的继承,至关于实现类继承了一个完全抽象的类(至关于除了默认方法外,全部方法都是抽象方法的类)。
//定义一个Product接口 interface Product { int getProduceTime(); } //让Printer类实现Output和Product接口 public class Printer implements Output, Product { private String[] printData = new String[MAX_CACHE_LINE]; //用以记录当前需打印的做业数 private int dataNum = 0; public void out() { //只要有做业,就继续打印 while(dataNum >0) { System.out.println("打印机打印:"+printData[0]); //把做业队列总体前移一位,并将剩下的做业数减1 System.arraycopy(printData, 1, printData, 0, --dataNum); } } public void getData(String msg) { if (dataNum >= MAX_CACHE_LINE) { System.out.println("输出队列已满,添加失败"); } else { //把打印数据添加到队列里,已保存数据的数量加1 printData[dataNum++] = msg; } } public int getProduceTime() { return 45; } public static void main(String[] args) { //建立一个Printer对象,当成Output使用 Output o = new Printer(); o.getData("Pringles品客薯片"); o.getData("酸乳酪洋葱味薯片"); o.out(); o.getData("乐天熊仔饼"); o.getData("小熊饼"); o.out(); //调用Output接口中定义的默认方法 o.print("大天狗","妖刀姬","一目连"); o.test(); //建立一个Printer对象,当成Product使用 Product p = new Printer(); System.out.println(p.getProduceTime()); //全部接口类型的引用变量均可直接赋给Object类型的变量 Object object = p; } }
实现接口方法时,必须使用public访问控制修饰符,由于接口里的方法都是public的,而子类(至关于实现类)重写父类方法时访问权限只能更大或者相等,因此实现类实现接口里的方法时只能使用public访问权限
接口和抽象类很像,它们都具备以下特征。
接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其余类实现和继承。
接口和抽象类均可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
接口做为系统与外界交互的窗口,接口体现的是一种规范
抽象类做为系统中多个子类的共同父类,它所体现的是一种模板式设计
接口和抽象类在用法上的差异:
接口里只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类则彻底能够包含普通方法
接口里不能定义静态方法;抽象类里能够定义静态方法
接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既能够定义普通成员变量,也能够定义静态常量
接口里不包含构造器;抽象类里能够包含构造器,抽象类里的构造器并非用于建立对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操做
接口里不能包含初始化块,但抽象类则彻底包含初始化块
一个类最多只能有一个直接父类,包括抽象类;但一个类能够直接实现多个接口,经过实现多个接口能够弥补Java单继承的不足。
接口体现的是一种规范和实现分离的设计哲学,充分利用接口能够极好地下降程序各模块之间的耦合,从而提升系统的可扩展性和可维护性
1.简单工厂模式
所谓设计模式,就是对常常出现的软件设计问题的成熟解决方案。
Computer类组合一个Output类型的对象,将Computer类与Printer类彻底分离。Computer对象实际组合的是Printer对象仍是BetterPrinter对象,对Computer而言彻底透明。当Printer对象切换到BetterPrinter对象时,系统彻底不受影响
public class Computer { private Output output; public Computer(Output output) { this.output = output; } // 定义一个模拟获取字符串输入的方法 public void keyIn(String msg) { output.getData(msg); } //定义一个模拟打印的方法 public void print() { output.out(); } }
Computer类已经彻底与Printer类分离,只是与Output接口耦合。Computer再也不负责建立Output对象,系统提供一个Output工厂来负责生成Output对象
public class OutputFactory { public Output getOutput() { return new Printer(); } public static void main(String[] args) { OutputFactory outputFactory = new OutputFactory(); Computer computer = new Computer(outputFactory.getOutput()); computer.keyIn("眼前的黑是什么黑,你说的白是什么白"); computer.keyIn("人们说的天空蓝,是我记忆中那团白云背后的蓝天"); computer.print(); } }
在该OutputFactory类中包含了一个getOutput()方法,该方法返回一个Output实现类的实例,该方法负责建立Output实例,具体建立哪个实现类的对象由该方法决定(具体由该方法中的粗体部分控制,固然也能够增长更复杂的控制逻辑)。若是系统须要将Printer改成BetterPrinter实现类,只需让BetterPrinter实现Output接口,并改变OutputFactory类中的getOutput()方法便可。
public class BetterPrinter implements Output { private String[] printData = new String[MAX_CACHE_LINE]; //用以记录当前需打印的做业数 private int dataNum = 0; public void out() { //只要有做业,就继续打印 while (dataNum > 0) { System.out.println("高速打印机正在打印:"+printData[0]); //把做业队列总体前移一位,并将剩下的做业数减1 System.arraycopy(printData, 1, printData, 0, --dataNum); } } public void getData(String msg) { if (dataNum >= MAX_CACHE_LINE * 2) { System.out.println("输出队列已满,添加失败"); } else { //把打印数据添加到队列里,已保存数据的数量加1 printData[dataNum++] = msg; } } }
上面的BetterPrinter类也实现了Output接口,所以也可当成Output对象使用,因而只要把OutputFactory工厂类的getOutput()方法中部分改成以下代码:
return new BetterPrinter();
经过这种方式,便可把全部生成Output对象的逻辑集中在OutputFactory工厂类中管理,而全部须要使用Output对象的类只需与Output接口耦合,而不是与具体的实现类耦合。即便系统中有不少类使用了Printer对象;只需OutputFactory类的getOutput()方法生成的Output对象是BetterPrinter对象,则它们所有都会改成使用BetterPrinter对象,而全部程序无须修改,只须要修改OutputFactory工厂类的getOutput()方法实现便可
2.命令模式
某个方法须要完成某一个行为,但这个行为的具体实现没法肯定,必须等到执行该方法时才能够肯定。具体一点:假设有个方法须要遍历某个数组元素,但没法肯定在遍历数组元素时如何处理这些元素,须要在调用该方法时指定具体的处理行为。
使用一个Command接口来定义一个方法,用这个方法来封装“处理行为”,但这个方法没有方法体——由于如今还没法肯定这个处理行为。
public interface Command { //接口里定义的process方法用于封装“处理行为” void process(int[] target); }
下面是须要处理数组的处理类,在这个处理中包含了一个process()方法,这个方法没法肯定处理数组的处理行为,因此定义该方法时使用了一个Command参数,这个Command参数负责对数组的处理行为。
public class ProcessArray { public void process(int[] target, Command cmd) { cmd.process(target); } }
经过一个Command接口,就实现了让ProcessArray类和具体“处理行为”的分离,程序使用Command接口表明了对数组的处理行为。Command接口也没有提供真正的处理,只要等到调用ProcessArray对象的process()方法时,才真正传入一个Command对象,才肯定对数组的处理行为。
public class CommandTest { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] target = {3, -4, 6, 4}; //第一次处理数组,具体处理行为取决于PrintCommand pa.process(target, new PrinterCommand()); System.out.println("-------"); pa.process(target, new AddCommand()); } }
public class PrinterCommand implements Command { public void process(int[] target) { for(int tmp :target) { System.out.println("迭代输出目标数组的元素:"+tmp); } } }
public class AddCommand implements Command { public void process(int[] target) { int sum = 0; for (int tmp : target) { sum += tmp; } System.out.println("数组元素的总和是"+sum); } }
一个类放在另外一个类的内部定义,这个定义在其余类内部的类就被称为内部类,包含内部类的类也被称为外部类。内部类主要有以下做用:
内部类提供了更好的封装,能够把内部类隐藏在外部类以内,不容许同一个包中的其余类访问该类。
内部类成员能够直接访问外部类的私有数据,由于内部类被当成其外部类成员,同一个类的成员之间能够互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
匿名内部类适合用于建立那些仅须要一次使用的类。
内部类与外部类的区别:
内部类比外部类能够多使用三个修饰符:private、protected、static——外部类不可使用这三个修饰符。
非静态内部类不能拥有静态成员。
定义内部类很是简单,只要把一个类放在另外一个类的内部定义便可。这次的“类内部”包括类中的任何位置,甚至在方法中也能够定义内部类(方法里定义的内部类被称为局部内部类)。内部类定义语法格式以下:
public class OuterClass { //此处能够定义内部类 }
大部分时候,内部类都被做为成员内部类定义,而不是做为局部内部类。成员内部类是一种与成员变量、方法、构造器和初始化块类似的类成员:局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类
外部类的上一级程序单元是包,因此它只有2个做用域:同一包内和任何位置。所以只需2种访问权限:包访问和公开访问权限,正好对应省略访问控制符和public访问控制符。省略访问控制符是包访问权限,即同一包中的其余类能够访问省略访问控制符的成员。所以若是一个外部类不使用任何访问控制符,则只能被同一个包中其余类访问。而内部类的上一级程序单元是外部类,他就具备4个做用域:同一个类、同一个包、父子类和任何位置,所以可使用4种访问控制权限。
成员内部类(包括静态内部类、非静态内部类)的class文件老是这种形式:OuterClass$InnerClass.class
在非静态内部类里能够直接访问外部类的private成员。由于在非静态内部类对象里,保存了一个它所寄生的外部类对象的引用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)
当在非静态内部类的方法访问某个变量时,系统优秀在该方法内查找是否存在该名字的局部变量,若是存在就使用该变量;若是不存在,则到该方法所在的内部类中查找是否存在该名字的成员变量,若是存在则使用该成员变量;若是不存在,则在该内部类所在的外部类中查找是否存在该名字的成员变量,若是存在则使用该成员变量;若是依然不存在,系统将出现编译错误;提示找不到该变量。
所以,若是外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可经过使用this、外部类类名.this做为限定来区分。
public class DiscernVariable { private String prop = "外部类的实例变量"; private class InClass { private String prop = "内部类的实例变量"; public void info() { String prop = "局部变量"; // 经过外部类类名.this.varName 访问外部类实例变量 System.out.println("外部类的实例变量值:"+DiscernVariable.this.prop); // 经过this.varName 访问内部类实例变量 System.out.println("内部类的实例变量值:"+this.prop); // 直接访问局部变量 System.out.println("局部变量值:"+prop); } } public void test() { InClass inClass = new InClass(); inClass.info(); } public static void main(String[] args) { new DiscernVariable().test(); } }
非静态内部类的成员能够访问外部类的private成员,但反过来就不成立了。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。若是外部类须要访问非静态内部类的成员,则必须显式建立非静态内部类对象来调用访问其实例成员。
public class Outer { private int outProp = 9; class Inter { private int inProp = 5; public void accessOuterProp() { //非静态内部类能够直接访问外部类的private成员变量 System.out.println("外部类的outProp值:"+outProp); } } public void accessInterProp() { //外部类不能直接访问非静态内部类的实例变量 //下面代码出现编译错误 //System.out.println("内部类的inProp值:"+inProp); //如需访问内部类的实例变量,必须显式建立内部类对象 System.out.println("内部类的inProp值:"+new Inter().inProp); } public static void main(String[] args) { //执行下面代码,只建立了外部类对象,还未建立内部类对象 Outer outer = new Outer(); outer.accessInterProp(); } }
非静态内部类对象必须寄生在外部类对象里,而外部类对象则没必要必定有非静态内部类对象寄生其中。简单地讲,若是存在一个非静态内部类对象,则必定存在一个被他寄生的外部类对象。但外部类对象存在时,外部类对象里不必定寄生了非静态内部类对象。所以外部类对象访问非静态内部类成员时,可能非静态普通内部类对象根本不存在!而非静态内部类对象访问外部类成员时,外部类对象必定存在。
根据静态成员不能访问非静态成员的规则,外部类的静态成员、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、建立实例等。总之,不容许在外部类的静态成员中直接使用非静态内部类。
Java不容许在非静态内部类定义静态成员。非静态内部类里不能有静态方法、静态成员变量、静态初始化块。
若是使用static来修饰一个内部类,则这个内部类就属于外部类自己,而不属于外部类的某个对象。所以使用static修饰的内部类被称为类内部类,有的地方也称为静态内部类。
static关键字的做用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。外部类的上一级程序单元是包,因此不可以使用static修饰;而内部类的上一级程序单元是外部类,使用static修饰能够将内部类变成外部类相关,而不是外部类实例相关。所以static关键字不可修饰外部类,但可修饰内部类。
静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就是说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的类自己中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,没有持有外部类对象的引用。若是容许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引发错误。
静态内部类是外部类的一个静态成员,所以外部类的全部方法、全部初始化块中可使用静态内部类来定义变量、建立对象等。
外部类依然不能直接访问静态内部类的成员,但可使用静态内部类的类名做为调用者来访问静态内部类的类成员,也可使用静态内部类对象做为调用者来访问静态内部类的实例成员。
1.在外部类使用内部类
能够直接经过内部类类名来定义变量,经过new调用内部类构造器来建立实例。 区别:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,由于静态成员不能访问非静态成员。
2.在外部类之外使用内部类
但愿在外部类之外的地方访问内部类(静态、非静态),则内部类不能使用private访问控制权限,private修饰的内部类只能在外部类内部使用。 ▲ 省略访问控制符的内部类,只能被与外部类处于同一个包中的其余类访问。 ▲ 使用protected修饰的内部类,可被与外部类处于同一个包中的其余类和外部类的子类所访问。 ▲ 使用public修饰的内部类,能够在任何地方被访问。 在外部类之外的地方定义内部类(包括静态和非静态)变量的语法格式以下: OuterClass.InnerClass varName 在外部类之外的地方建立非静态内部类实例的语法: OuterInstance.new InnerConstructor() class Out { //定义一个内部类,不使用访问控制符 //即只与同一个包中的其余类可访问该内部类 class In { public In(String msg) { System.out.println(msg); } } } public class CreateInnerInstance { public static void main(String[] args) { Out.In in = new Out().new In("瞅啥瞅"); /* 上面代码可改成以下三行代码 使用OuterClass.InerClass的形式定义内部类变量 Out.In in; 建立外部类实例,非静态内部类实例将寄生在该实例中 Out out = new Out(); 经过外部类实例和new来调用内部类构造器建立非静态内部类实例 in = out.new In("瞅啥瞅"); */ } }
定义一个子类继承Out类的非静态内部类In类
public class SubClass2 extends Out.In { //显式定义SubClass2的构造器 public SubClass2(Out out) { //经过传入的Out对象显式调用In的构造器 out.super("瞅你咋地"); } } 非静态内部类In类的构造器必须使用外部类对象来调用,代码中super表明调用In类的构造器,而out则表明外部类对象(上面的Out、In两个类直接来自前一个CreateInnerInsatence.java)。能够看出若是须要建立SubClass2对象时,必须先建立一个Out对象。由于SubClass2是非静态内部类In类的子类,非静态内部类In对象里必须与一个对Out对象的引用,其子类SubClass2对象里也应该持有对Out对象的引用。当建立SubClass2对象时传给该构造器的Out对象,就是SubClass2对象里Out对象引用所指向的对象。 非静态内部类In对象和SubClass2对象都必须持有指向Outer对象的引用,区别是建立两种对象时传入Out对象的方式不一样:当建立非静态内部类In类的对象时,必须经过Outer对象来调用new关键字;当建立SubClass2类的对象时,必须使用Outer对象做为调用者来调用In类的构造器。
3.在外部类之外使用静态内部类
在外部类之外的地方建立静态内部类实例的语法: new OutClass.InnerConstructor() class StaticOut { //定义一个静态内部类,不使用访问控制符 //即同一个包中的其余类可访问该内部类 static class StaticIn { public StaticIn() { System.out.println("静态内部类的构造器"); } } } public class CreateStaticInnerInstance { public static void main(String[] args) { StaticOut.StaticIn in = new StaticOut.StaticIn(); /* 上面代码可改成以下两行代码 使用OuterClass.InnerClass的形式定义内部类变量 StaticOut.StaticIn in; 经过new来调用内部构造器建立静态内部类实例 in = new StaticOut.StaticIn(); */ } } 无论是静态内部类仍是非静态内部类,它们声明变量的语法彻底同样。区别只是在建立内部类对象时,静态内部类只需使用外部类便可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。
使用静态内部类比使用非静态内部类要简单不少,只要把外部类当成静态内部类的包空间便可。所以当程序须要使用内部类时,应该优先考虑使用静态内部类。
若是把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。因为局部内部类不能在外部类的方法之外的地方使用,所以局部内部类也不能使用访问控制符和static修饰符修饰。
对于局部成员而言,无论是局部变量仍是局部内部类,它们的上一级程序单元都是方法,而不是类,使用static修饰它们没有任何意义。所以,全部的局部成员都不能使用static修饰。
匿名内部类适合建立只须要一次使用的类。建立匿名内部类时会当即建立一个该类的实例,这个类定义当即消失,匿名内部类不能重复使用。
new 实现接口() | 父类构造器(实参列表) { //匿名内部类的类体部分 }
关于匿名内部类规则
匿名内部类不能是抽象类,由于系统在建立匿名内部类时,会当即建立匿名内部类的对象。所以不容许将匿名内部类定义成抽象类
匿名内部类不能定义构造器。因为匿名内部类没有类名,因此没法定义构造器,但匿名内部类能够定义初始化块,能够经过实例初始化块来完成构造器须要完成的事情。
最经常使用的建立匿名内部类的方式是须要建立某个接口类型的对象
interface Product { public double getPrice(); public String getName(); } public class AnonymousTest { public void test(Product p) { System.out.println("购买了一个"+p.getName() +",花掉了"+p.getPrice()); } public static void main(String[] args) { AnonymousTest ta = new AnonymousTest(); //调用test()方法时,须要传入一个Product参数 //此处传入其匿名实现类的实例 ta.test(new Product() { public double getPrice() { return 567.8; } public String getName() { return "AGP显卡"; } }); } }
上面程序中的AnonymousTest类定义了一个test()方法,该方法须要一个Product对象做为参数,但Product是接口,没法直接建立对象,所以这次建立一个Product接口实现类的对象传入该方法
若是这个Product接口实现类只需使用一次,则采用上述方式,定义匿名内部类
当经过实现接口来建立匿名内部类时,匿名内部类也不能显式建立构造器,所以匿名内部类只有一个隐式的无参数构造器,故new接口名后的括号里不能传入参数值
若是经过继承父类来建立匿名内部类时,匿名内部类将拥有和父类类似的构造器,这次的类似指的是拥有相同的形参列表
当建立匿名内部类时,必须实现接口或抽象父类里的全部抽象方法
abstract class Device { private String name; public abstract double getPrice(); public Device(){}; public Device(String name) { this.name =name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class AnonymousInner { public void test(Device d) { System.out.println("购买了一个"+d.getName()+",花掉了"+d.getPrice()); } public static void main(String[] args) { AnonymousInner ai = new AnonymousInner(); //调用有参数的构造器建立Device匿名内部类的对象 ai.test(new Device("电子示波器") { public double getPrice() { return 67.8; } }); //调用无参数的构造器建立Device匿名内部类的对象 Device d = new Device() { //初始化块 { System.out.println("匿名内部类的初始化块..."); } //实现抽象方法 public double getPrice() { return 56.2; } public String getName() { return "键盘"; } }; ai.test(d); } }
当建立Device为父类的匿名内部类时,既能够传入参数,表明父类带参数的构造器;也能够不传入参数,表明调用父类无参数的构造器。
interface A { void test(); } public class ATest { public static void main(String[] args) { int age = 8; A a = new A() { public void test() { //在Java 8之前下面语句将提示错误:age必须使用final修饰 //从Java 8开始,匿名内部类、局部内部类容许访问非final局部变量 System.out.println(age); } }; a.test(); } }
若是局部变量被匿名内部类访问,那么局部变量至关于自动使用了final修饰。
Java 8 新增了接口的默认方法。简单说,默认方法就是接口能够有实现方法,并且不须要实现类去实现其方法,只需在方法名前面加个default关键字便可实现默认方法。
默认方法语法格式以下:
public interface PPAP { default void print() { System.out.println("I have an apple, I have a pen~"); } }