• 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
  • 相关阅读:
    网络面试题2
    网络
    Linux os
    操作系统面试题2
    操作系统面试题
    Linux
    算法-字符全排列
    第k大数问题
    地址
    ListView里面嵌套CheckBox
  • 原文地址:https://www.cnblogs.com/chenyg32/p/3333734.html
Copyright © 2020-2023  润新知