Java知识点4

Java内部类及其实例化

在 Java 中,允许在一个类(或方法、语句块)的内部定义另一个类,称为内部类(Inner Class),有时也称为嵌套类(Nested Class)。

使用内部类的主要原因有:
• 内部类可以访问外部类中的数据,包括私有的数据。
• 内部类可以对同一个包中的其他类隐藏起来。
• 当想要定义一个回调函数且不想编写大量代码时,使用匿名(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);
    }
}

注意:必须先有外部类的对象才能生成内部类的对象,因为内部类需要访问外部类中的成员变量,成员变量必须实例化才有意义。

Java静态内部类、匿名内部类、成员式内部类和局部内部类
内部类可以是静态(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();
    }
}

成员式内部类如同外部类的一个普通成员。 成员式内部类可以使用各种修饰符,包括 public、protected、private、static、final 和 abstract,也可以不写。若有 static 修饰符,就为类级,否则为对象级。类级可以通过外部类直接访问,对象级需要先生成外部的对象后才能访问。    非静态内部类中不能声明任何 static 成员
class A {
    // B、C 间可以互相调用
    class B {}
    class C {}
}

更多见:http://www.weixueyuan.net/view/6007.html#
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
匿名内部类是局部内部类的一种特殊形式,也就是没有变量名指向这个类的实例,而且具体的类实现会写在这个内部类里面。匿名类必须继承一个父类或实现一个接口。
不使用匿名内部类来实现抽象方法:
abstract class Person {
    public abstract void eat();
}

class Child extends Person {
    public void eat() {
        System.out.println("eat something");
    }
}

public class Demo {
    public static void main(String[] args) {
        Person p = new Child();
        p.eat();
    }
}
运行结果:
eat something

可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用。但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类。使用匿名内部类实现:
abstract class Person {
    public abstract void eat();
}

public class Demo {
    public static void main(String[] args){
      
        // 继承 Person 类
        new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        }.eat();
    }
}
可以看到,匿名类继承了 Person 类并在大括号中实现了抽象类的方法。

Abstract
在自上而下的继承层次结构中,位于上层的类更具有通用性,甚至可能更加抽象。从某种角度看,祖先类更加通用,它只包含一些最基本的成员,人们只将它作为派生其他类的基类,而不会用来创建对象。甚至,你可以只给出方法的定义而不实现,由子类根据具体需求来具体实现。

这种只给出方法定义而不具体实现的方法被称为 抽象方法,抽象方法是没有方法体的,在代码的表达上就是没有“{}”。包含一个或多个抽象方法的类也必须被声明为 抽象类

使用 abstract 修饰符来表示抽象方法和抽象类。

抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。

抽象类不能被实例化,抽象方法必须在子类中被实现。请看下面的代码:
import static java.lang.System.*;

public final class Demo{
    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();
}

class Teacher extends People{
    // 必须实现该方法
    public void work(){
        out.println("我的名字叫" + this.getName() + ",我正在讲课,请大家不要东张西望...");
    }
}

class Driver extends People{
    // 必须实现该方法
    public void work(){
        out.println("我的名字叫" + this.getName() + ",我正在开车,不能接听电话...");
    }
}
关于抽象类的几点说明:
  • 抽象类不能直接使用,必须用子类去实现抽象类,然后使用其子类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例,也就是可以使用抽象类来充当形参,实际实现类作为实参,也就是多态的应用。
  • 不能有抽象构造方法或抽象静态方法。 
在下列情况下,一个类将成为抽象类:
  1. 当一个类的一个或多个方法是抽象方法时;
  2. 当类是一个抽象类的子类,并且不能为任何抽象方法提供任何实现细节或方法主体时;
  3. 当一个类实现一个接口,并且不能为任何抽象方法提供实现细节或方法主体时;注意:
    • 这里说的是这些情况下一个类将成为抽象类,没有说抽象类一定会有这些情况。
    • 一个典型的错误:抽象类一定包含抽象方法。 但是反过来说“包含抽象方法的类一定是抽象类”就是正确的。
    • 事实上,抽象类可以是一个完全正常实现的类
注:抽象类里的方法不能是静态的,任何类的构造函数也不能是静态的。静态的方法不能操作对象,而抽象类必须由抽象类的子类(也是抽象类)实例化后,去实现里面的抽象方法。可以用抽象类作为形参,其子类的实现作为实参,这也是多态的应用。
interface
在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。
现实中也有很多接口的实例,比如说串口电脑硬盘,Serial ATA委员会指定了Serial ATA 2.0规范,这种规范就是接口。Serial ATA委员会不负责生产硬盘,只是指定通用的规范。

希捷、日立、三星等生产厂家会按照规范生产符合接口的硬盘,这些硬盘就可以实现通用化,如果正在用一块160G日立的串口硬盘,现在要升级了,可以购买一块320G的希捷串口硬盘,安装上去就可以继续使用了。

下面的代码可以模拟Serial ATA委员会定义以下串口硬盘接口:

    
    
  1. //串行硬盘接口
  2. public interface SataHdd{
  3. //连接线的数量
  4. public static final int CONNECT_LINE=4;
  5. //写数据
  6. public void writeData(String data);
  7. //读数据
  8. public String readData();
  9. }
注意:接口中声明的成员变量默认都是 public static final 的,必须显示的初始化。因而在常量声明时可以省略这些修饰符。
1) 接口中只能定义抽象方法,这些方法默认为 public abstract 的,因而在声明方法时可以省略这些修饰符。试图在接口中定义实例变量、非抽象的实例方法及静态方法,都是非法的。例如:

     
     
  1. public interface SataHdd{
  2. //连接线的数量
  3. public int connectLine; //编译出错,connectLine被看做静态常量,必须显式初始化
  4. //写数据
  5. protected void writeData(String data); //编译出错,必须是public类型
  6. //读数据
  7. public static String readData(){ //编译出错,接口中不能包含静态方法
  8. return "数据"; //编译出错,接口中只能包含抽象方法,
  9. }
  10. }


3) 接口中没有构造方法,不能被实例化

4) 一个接口不实现另一个接口,但可以继承多个其他接口。接口的多继承特点弥补了类的单继承。例如:


    
    
  1. //串行硬盘接口
  2. public interface SataHdd extends A,B{
  3. // 连接线的数量
  4. public static final int CONNECT_LINE = 4;
  5. // 写数据
  6. public void writeData(String data);
  7. // 读数据
  8. public String readData();
  9. }
  10. interface A{
  11. public void a();
  12. }
  13. interface B{
  14. public void b();
  15. }


为什么使用接口

接口是可插入性的保证。在一个继承链中的任何一个类都可以实现一个接口,这个接口会影响到此类的所有子类,但不会影响到此类的任何父类。此类将不得不实现这个接口所规定的方法,而子类可以从此类自动继承这些方法,这时候,这些子类具有了可插入性。

接口的使用:接口的使用与类的使用有些不同。在需要使用类的地方,会直接使用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 运行时系统会动态地确定应该使用哪个类中的方法,实际上是调用相应的实现类的方法。

示例如下:



   
   
  1. public class Demo{
  2. public void test1(A a) {
  3. a.doSth();
  4. }
  5. public static void main(String[] args) {
  6. Demo d = new Demo();
  7. A a = new B();
  8. d.test1(a);
  9. }
  10. }
  11. interface A {
  12. public int doSth();
  13. }
  14. class B implements A {
  15. public int doSth() {
  16. System.out.println("now in B");
  17. return 123;
  18. }

运行结果:
now in B

大家看到接口可以作为一个类型来使用,把接口作为方法的参数和返回类型。
(--Java接口与抽象类就是多态性技术来实现的,明白了多态特性对于为什么将接口作为参数化传入以解决业务的方式的好处了。这其中还有牵涉到一个问题,就是设计模式的问题。接口技术,其实就是采用一种”工厂设计模式“,它与动态代理相结合的时候可以让我们的参数接受更为广泛的数据形式,达到一种“兼容”来处理数据,增强程序数据处理能力。--)

---------------------------------下面说一下抽象类和接口的主要区别。---------------------------------

1) 抽象类可以为部分方法提供实现,避免了在子类中重复实现这些方法,提高了代码的可重用性,这是抽象类的优势;而接口中只能包含抽象方法,不能包含任何实现。


    
    
  1. public abstract class A{
  2. public abstract void method1();
  3. public void method2(){
  4. //A method2
  5. }
  6. }
  7. public class B extends A{
  8. public void method1(){
  9. //B method1
  10. }
  11. }
  12. public class C extends A{
  13. public void method1(){
  14. //C method1
  15. }
  16. }
抽象类A有两个子类B、C,由于A中有方法method2的实现,子类B、C中不需要重写method2方法,我们就说A为子类提供了公共的功能,或A约束了子类的行为。method2就是代码可重用的例子。A 并没有定义 method1的实现,也就是说B、C 可以根据自己的特点实现method1方法,这又体现了松散耦合的特性。
再换成接口看看:

      
      
  1. public interface A{
  2. public void method1();
  3. public void method2();
  4. }
  5. public class B implements A{
  6. public void method1(){
  7. //B method1
  8. }
  9. public void method2(){
  10. //B method2
  11. }
  12. }
  13. public class C implements A{
  14. public void method1(){
  15. //C method1
  16. }
  17. public void method2(){
  18. //C method2
  19. }
  20. }
接口A无法为实现类B、C提供公共的功能,也就是说A无法约束B、C的行为。B、C可以自由地发挥自己的特点现实 method1和 method2方法,接口A毫无掌控能力。
2) 一个类只能继承一个直接的父类(可能是抽象类),但一个类可以实现多个接口,这个就是接口的优势。

综上所述,接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:
  • 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
  • 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值