• 内部类可以访问外部类中的数据,包括私有的数据。
• 内部类可以对同一个包中的其他类隐藏起来。
• 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
• 减少类的命名冲突
public class Outer {
private int size;
public class Inner {
private int counter = 10;
public void doStuff() {
size++;
}
}
public static void main(String args[]) {
Outer outer = new Outer();
Inner inner = outer.new Inner();
inner.doStuff();
System.out.println(outer.size);
System.out.println(inner.counter);
// 编译错误,外部类不能访问内部类的变量
System.out.println(counter);
}
}
内部类可以是静态(static)的,可以使用 public、protected 和 private 访问控制符,而外部类只能使用 public,或者默认。成员式内部类
在外部类内部直接定义(不在方法内部或代码块内部)的类就是成员式内部类,它可以直接使用外部类的所有变量和方法,即使是 private 的。外部类要想访问内部类的成员变量和方法,则需要通过内部类的对象来获取。
public class Outer{
private int size;
public class Inner {
public void dostuff() {
size++;
}
}
public void testTheInner() {
Inner in = new Inner();
in.dostuff();
}
}
// B、C 间可以互相调用
class B {}
class C {}
}
public abstract void eat();
}
public void eat() {
System.out.println("eat something");
}
}
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
eat something
可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用。但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类。使用匿名内部类实现:
public abstract void eat();
}
public static void main(String[] args){
// 继承 Person 类
new Person() {
public void eat() {
System.out.println("eat something");
}
}.eat();
}
}
这种只给出方法定义而不具体实现的方法被称为 抽象方法,抽象方法是没有方法体的,在代码的表达上就是没有“{}”。包含一个或多个抽象方法的类也必须被声明为 抽象类。
使用 abstract 修饰符来表示抽象方法和抽象类。
抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。
抽象类不能被实例化,抽象方法必须在子类中被实现。请看下面的代码:
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("王明");
t.work();
Driver d = new Driver();
d.setName("小陈");
d.work();
}
}
abstract class People{
private String name; // 实例变量
// 共有的 setter 和 getter 方法
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
// 抽象方法
public abstract void work();
}
// 必须实现该方法
public void work(){
out.println("我的名字叫" + this.getName() + ",我正在讲课,请大家不要东张西望...");
}
}
// 必须实现该方法
public void work(){
out.println("我的名字叫" + this.getName() + ",我正在开车,不能接听电话...");
}
}
- 抽象类不能直接使用,必须用子类去实现抽象类,然后使用其子类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例,也就是可以使用抽象类来充当形参,实际实现类作为实参,也就是多态的应用。
- 不能有抽象构造方法或抽象静态方法。
- 当一个类的一个或多个方法是抽象方法时;
- 当类是一个抽象类的子类,并且不能为任何抽象方法提供任何实现细节或方法主体时;
- 当一个类实现一个接口,并且不能为任何抽象方法提供实现细节或方法主体时;注意:
- 这里说的是这些情况下一个类将成为抽象类,没有说抽象类一定会有这些情况。
- 一个典型的错误:抽象类一定包含抽象方法。 但是反过来说“包含抽象方法的类一定是抽象类”就是正确的。
- 事实上,抽象类可以是一个完全正常实现的类
希捷、日立、三星等生产厂家会按照规范生产符合接口的硬盘,这些硬盘就可以实现通用化,如果正在用一块160G日立的串口硬盘,现在要升级了,可以购买一块320G的希捷串口硬盘,安装上去就可以继续使用了。
下面的代码可以模拟Serial ATA委员会定义以下串口硬盘接口:
- //串行硬盘接口
- public interface SataHdd{
- //连接线的数量
- public static final int CONNECT_LINE=4;
- //写数据
- public void writeData(String data);
- //读数据
- public String readData();
- }
- public interface SataHdd{
- //连接线的数量
- public int connectLine; //编译出错,connectLine被看做静态常量,必须显式初始化
- //写数据
- protected void writeData(String data); //编译出错,必须是public类型
- //读数据
- public static String readData(){ //编译出错,接口中不能包含静态方法
- return "数据"; //编译出错,接口中只能包含抽象方法,
- }
- }
3) 接口中没有构造方法,不能被实例化
4) 一个接口不实现另一个接口,但可以继承多个其他接口。接口的多继承特点弥补了类的单继承。例如:
- //串行硬盘接口
- public interface SataHdd extends A,B{
- // 连接线的数量
- public static final int CONNECT_LINE = 4;
- // 写数据
- public void writeData(String data);
- // 读数据
- public String readData();
- }
- interface A{
- public void a();
- }
- interface B{
- public void b();
- }
为什么使用接口
接口是可插入性的保证。在一个继承链中的任何一个类都可以实现一个接口,这个接口会影响到此类的所有子类,但不会影响到此类的任何父类。此类将不得不实现这个接口所规定的方法,而子类可以从此类自动继承这些方法,这时候,这些子类具有了可插入性。
接口的使用:接口的使用与类的使用有些不同。在需要使用类的地方,会直接使用new关键字来构建一个类的实例,但接口不可以这样使用,因为接口不能直接使用 new 关键字来构建实例。接口必须通过类来实现(implements)它的抽象方法,然后再实例化类。类实现接口的关键字为implements。
如果一个类不能实现该接口的所有抽象方法,那么这个类必须被定义为抽象方法一个类只能继承一个父类,但却可以实现多个接口。
实现接口的格式如下:
修饰符 class 类名 extends 父类 implements 多个接口 {
实现方法
}
-----------------------------------------------
import static java.lang.System.*;
public class Demo{
public static void main(String[] args) {
SataHdd sh1=new SeagateHdd(); //初始化希捷硬盘
SataHdd sh2=new SamsungHdd(); //初始化三星硬盘
}
}
//串行硬盘接口
interface SataHdd{
//连接线的数量
public static final int CONNECT_LINE=4;
//写数据
public void writeData(String data);
//读数据
public String readData();
}
// 维修硬盘接口
interface fixHdd{
// 维修地址
String address = "北京市海淀区";
// 开始维修
boolean doFix();
}
//希捷硬盘
class SeagateHdd implements SataHdd, fixHdd{
//希捷硬盘读取数据
public String readData(){
return "数据";
}
//希捷硬盘写入数据
public void writeData(String data) {
out.println("写入成功");
}
// 维修希捷硬盘
public boolean doFix(){
return true;
}
}
//三星硬盘
class SamsungHdd implements SataHdd{
//三星硬盘读取数据
public String readData(){
return "数据";
}
//三星硬盘写入数据
public void writeData(String data){
out.println("写入成功");
}
}
//某劣质硬盘,不能写数据
abstract class XXHdd implements SataHdd{
//硬盘读取数据
public String readData() {
return "数据";
}
}
---------------------------------------------------------------
接口作为类型使用
接口作为引用类型来使用,任何实现该接口的类的实例都可以存储在该接口类型的变量中,通过这些变量可以访问类中所实现的接口中的方法,Java 运行时系统会动态地确定应该使用哪个类中的方法,实际上是调用相应的实现类的方法。
示例如下:
- public class Demo{
- public void test1(A a) {
- a.doSth();
- }
- public static void main(String[] args) {
- Demo d = new Demo();
- A a = new B();
- d.test1(a);
- }
- }
- interface A {
- public int doSth();
- }
- class B implements A {
- public int doSth() {
- System.out.println("now in B");
- return 123;
- }
-
运行结果:
now in B
大家看到接口可以作为一个类型来使用,把接口作为方法的参数和返回类型。
(--Java接口与抽象类就是多态性技术来实现的,明白了多态特性对于为什么将接口作为参数化传入以解决业务的方式的好处了。这其中还有牵涉到一个问题,就是设计模式的问题。接口技术,其实就是采用一种”工厂设计模式“,它与动态代理相结合的时候可以让我们的参数接受更为广泛的数据形式,达到一种“兼容”来处理数据,增强程序数据处理能力。--)
---------------------------------下面说一下抽象类和接口的主要区别。---------------------------------
1) 抽象类可以为部分方法提供实现,避免了在子类中重复实现这些方法,提高了代码的可重用性,这是抽象类的优势;而接口中只能包含抽象方法,不能包含任何实现。
- public abstract class A{
- public abstract void method1();
- public void method2(){
- //A method2
- }
- }
- public class B extends A{
- public void method1(){
- //B method1
- }
- }
- public class C extends A{
- public void method1(){
- //C method1
- }
- }
- public interface A{
- public void method1();
- public void method2();
- }
- public class B implements A{
- public void method1(){
- //B method1
- }
- public void method2(){
- //B method2
- }
- }
- public class C implements A{
- public void method1(){
- //C method1
- }
- public void method2(){
- //C method2
- }
- }
综上所述,接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:
- 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
- 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。