Java逍遥游记读书笔记<二>

Abstract抽象类

1.抽象类不能被实例化

2.抽象方法没有方法体

如:

public abstract class Weapen
{
    public abstract void attack(); 
} 
  • 抽象类中可以没有抽象方法,但若有了抽象方法,该类就必须是抽象类
  • abstract和static不能连用,为什么呢?请往下看……
  • 抽象类虽然不能被实例化,但是能创建一个引用变量,类型是抽象类,引用非抽象类的具体子类的一个实例。
abstract class Weapon
{
    public abstract void attack(); 
}

public class Sword extends Weapon
{
    public void attack()
    {
        //do sth.
    }
    
    public static void main(String args[])
    {
        //Weapon w1 = new Weapon();    //error
        
        Weapon w2 = new Sword();    //ok
    }
}
Sword.java 

 

final

  • final修饰的类不允许被继承,即没有子类,断后了。
  • final修饰的方法不能被子类的方法覆盖。
  • final修饰的变量表示常量,只允许被赋值一次。
  • final类型的实例变量,要么在定义变量的时候初始化,要么在构造方法中初始化。

  (假如有多个构造函数,每一个构造函数都要初始化final变量,否则会出现编译错误“错误可能尚未初始化变量”)

  • final类型的静态变量,只能在定义变量的时候初始化。

 

Static

由于静态方法不需要通过实现实例就可以被调用,所以在静态方法中不能使用this关键字(它不知道this指的是谁,因为没有实例);也不能直接访问实例变量和实例方法(没有实例哪来的实例变量和实例方法);但是可以直接访问静态变量和静态方法。

 

静态方法必须被实现,为什么

因为静态方法不依赖类的实例而存在,所以当前类必须为静态方法提供实现,换句话说,一个静态的方法不能定义为抽象的方法。即staticabstract不能同时使用。

public class Test
{
    int x;//x是一个实例变量,不能被静态方法直接访问
    
    Test()
    {
        x = 1;
    }
    
    public static void main(String[] args)
    {
        System.out.println(x);
    }
}
错误的Test.java
public class Test
{
    int x;
    
    Test()
    {
        x = 1;
    }
    
    public static void main(String[] args)
    {
        Test t1 = new Test();
        
        System.out.println(t1.x);
    }
}
正确的Test.java

接口

Seller.java

interface Cal_Int    //接口
{
    double mul(double a, double b);    //public abstract
}

class Cal implements Cal_Int    //实现接口的类
{
    public double mul(double a, double b)    //must public
    {
        return a*b;
    }
}

class Seller
{
    Cal_Int cal;
    
    public Seller(Cal_Int cal)
    {
        this.cal = cal;
    }
    
    public void compute(double price, double amount)
    {
        System.out.println(cal.mul(price, amount));
    }
    
    public static void main(String[] args)
    {
        Cal_Int cal = new Cal();        //定义接口类型的引用变量,引用了实现该接口的类的实例
        
        Seller seller = new Seller(cal);
        
        seller.compute(0.1, 16);
    }
}
View Code

通俗来讲,接口就是一个类似抽象类的东西。

如果一个类需要用到接口,那么该类只需按照接口给出的函数调用即可,而不关心它是怎么实现的(就像调用API一样)

如果一个类需要实现接口,那么该类就要覆盖(override)接口给出的函数。(Android中的implements OnClickListener,然后重写onClick函数

  • 接口中的成员变量默认且必须是public, static, final,必须被显示初始化。
  • 接口中的方法默认且必须是public abstract。
  • 接口没有构造方法。
public interface A
{
    public static final CONST2 = 2;
    
    int CONST = 1;        //ok    默认是public static final
        
    int var;            //error static未显示初始化 接口中也没有构造方法可以初始化
    
    void f();            //ok    默认是public, abstract
    
    void f2(){};        //error 必须是abstract
    
    protected void f3();//error    必须是public

    public A(){}        //error 接口中没有构造方法,因为没有实例
}
View Code
  • 一个接口不能实现另一个接口,但是可以继承多个接口。
  • 一个类只能继承一个父亲,但可以实现多个接口。
  • 接口必须通过类来实现它的抽象方法。
  • 当类实现接口的时候,必须实现接口中的所有方法,否则它将被定义为抽象类(不然就含有Abstract方法,那就必须是抽象类)。
  • 不能直接创建接口的实例,但是可以定义接口类型的引用变量,该变量引用实现了该接口的类的实例(好好理解下这句话,不懂的话见上面Seller.java)。

this与super

Monkey.java

public class Monkey
{
    private String name;
    
    private int age;
    
    public Monkey(String name, int age)
    {
        this.name = name;
        
        this.age = age;
    }
    
    public Monkey(String name)    //缺省age的时候
    {
        this(name, -1);            //ok
            
        //Monkey(name, -1);        //error 不能通过类名来调用
    }
    
    public Monkey(int age)        //缺省name的时候
    {
        this("Anonymous", age);
    }
    
    public Monkey()    
    {
        this("Anonymous", -1);    //缺省name和age的时候
    }
}
View Code
  • 同一个类中,在一个构造方法中,可以通过使用this来调用另一个重载的构造方法。而不能使用类名来调用,这点很重要。
  • this语句必须放在构造方法中的第一句。

Sub.java

class Base
{
    Base(int i)
    {
        System.out.println(i);
    }
}

public class Sub extends Base
{
    Sub(int i)
    {
        super(i);
    }
    
    Sub()
    {
        super(0);
    }
    
    public static void main(String[] args)
    {
        Sub sub = new Sub();
        
        Sub sub2 = new Sub(1);
    }
}
View Code

子类调用父类的构造方法一共有2种方式,一种是用super,另一种不使用super,系统自动调用父类的默认构造方法。

  • 子类的构造方法中不能使用父类名字来调用父类的构造方法,而要使用super语句。
  • super语句必须放在子类构造方法中的第一句。

内部类

内部类:在一个类的内部定义的类就称为内部类。

内部类可以有4种访问级别:publicprotected,默认和private

顶层类(最外层的类)只能有2种访问级别:public和默认。(你什么时候见过最外层的类有protectedprivate修饰)

 

 

类内:

变量可以分为局部变量和成员变量,成员变量可以分为静态变量和实例变量。

内部类可以分为局部内部类和成员内部类,成员内部类可以分为静态内部类和实例内部类。

 

局部内部类:

局部内部类不能用publicprotectedprivatestatic修饰。

局部内部类可以直接访问外部类的所有成员以及所在方法中的final参数和变量。

class A
{
    int a;
    
    public void f(final int p1, int p2)
    {
        final int v1 = 2;
        int v2 = 1;
        
        class B
        {
            int b1 = a;        //ok
            int b2 = p1;    //ok
            int b3 = p2;    //error
            int b4 = v1;    //ok
            int b5 = v2;    //error
        }
    }
}
View Code

实例内部类:

1.

public class A
{
    public class B
    {
    }
    
    private B b = new B();    //类内直接访问B 
}

A.B b = new A().new B();    //类外访问要确保类名完整 
View Code

这个例子中,内部类B的完整类名为A.B。若在类内,可以直接访问;若在类外,访问时类名要完整。

最后一句等价于

A a = new A();
A.B b = a.new B();

2.实例内部类可以直接访问外部类的所有成员变量和方法(任何级别的都可以)

为什么呢,因为当内部类B的实例存在的时候,它的外部类A的实例必定也存在,因此可以访问到。

可以设想一个儿子诞生的时候,必定有母亲的存在,所以他可以持有母亲有的东西。

3.外部类不能直接访问实例内部类的成员变量和方法,但可以访问实例内部类的实例的成员变量和方法。

这样理解,若一个母亲可能没有儿子,那这个时候怎么拿到儿子的东西呢?但是若是先创建了一个儿子,那么这个时候母亲就可以拿到了。

public class A
{
    public class B
    {
        int x = 0;
    }
    
    public void f()
    {
        int v1 = x;        //error    万一内部类的实例没有创建呢 
        
        B b = new B();
        
        int v2 = b.x;    //ok    通过实例去访问 
    }
}
View Code

静态内部类:

静态内部类用static修饰,不同于实例内部类,静态内部类的实例不依赖于外部类实例的存在。

1.创建静态内部类时,不需要创建外部类的实例。

public class A
{
    public static class B
    {
        int x = 0;
    }
}

A.B b = new A.B();
b.x = 2;
View Code

2.静态内部类可以直接访问外部类的静态成员,如果要访问实例成员,只能通过外部类的实例去访问。

public class A
{
    int a1;
    static int a2;
    
    public static class B
    {
        int b1 = a1;            //error
        
        int b2 = a2;            //ok 
        
        int b3 = new A().a1;    //ok
    }
}
View Code

3.可以通过完整的类名去访问静态内部类的静态成员。

public class A
{
    public static class B
    {
        int v1;
        
        static int v2;
    }
}

A.Bb = new A.B();
b.v1 = 1;            //ok
b.v2 = 2;             //ok
A.B.v1 = 1;            //error
A.B.v2 = 2;         //ok
View Code

匿名类:

匿名类:没有名字的内部类

public class A
{
    A()
    {
        System.out.println("Default Constructor");
    }
    
    A(int v)
    {
        System.out.println("Special Constructor");
    }
    
    void f()
    {
        System.out.println("from A");
    }
    
    public static void main(String[] args)
    {
        new A().f();
        
        A a = new A()    //定义了一个继承A的匿名类 
        {
            void f(){System.out.println("from anonymous");}//覆盖父类A的方法 
        };
        
        a.f();
    }
}
View Code

输出结果是

Default Constructor
from A
Default Constructor
from anonymous

这个语句

A a = new A()    //定义了一个继承A的匿名类 
{
    void f(){System.out.println("from anonymous");}//覆盖父类A的方法 
};

定义了一个继承A的匿名类。相当于

class Sub extends A
{
    void f(){System.out.println("from anonymous");}//覆盖父类A的方法 
}
A a = new Sub();

所以这种情况可以看作是没有名字的子类。

1.匿名类本身没有构造方法,但会自动调用父类的构造方法。

public class A
{
    A()
    {
        System.out.println("Default Constructor");
    }
    
    A(int v)
    {
        System.out.println("Special Constructor");
    }
    
    void f()
    {
        System.out.println("from A");
    }
    
    public static void main(String[] args)
    {
        int v = 1;
        A a = new A(v)    //定义了一个继承A的匿名类 
        {
            void f(){System.out.println("from anonymous");}//覆盖父类A的方法 
        };
        
        a.f();
    }
}
View Code

Special Constructor
from anonymous

注意,如果在匿名类中使用了局部变量或参数,那该变量或参数必须是final。例如

public class A
{
    A()
    {
        System.out.println("Default Constructor");
    }
    
    A(int v)
    {
        System.out.println("Special Constructor");
    }
    
    void f()
    {
        System.out.println("from A");
    }
    
    public static void main(String[] args)
    {
        final int v = 1;    //必须定义为final
        A a = new A(v)    
        {
            void f(){System.out.println(v);}//使用了局部变量 
        };
        
        a.f();
    }
}
View Code

巧记:匿名类和局部内部类一样,访问到局部参数和变量的时候都必须是final类型的。

 

2.匿名类除了继承类,还可以实现接口

public class A
{
    public static void main(String[] args)
    {
        Thread t = new Thread    //实现了Runnable接口 
        (
            new Runnable()
            {
                public void run(){}
            }
        );
        t.start();
    }
}
View Code
原文地址:https://www.cnblogs.com/chenyg32/p/3333734.html