在劫

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

抽象类

抽象方法与抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类可以没有抽象方法。

抽象方法和抽象类的规则:抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符修饰,抽象方法不能有方法体;抽象类不能实体化,无法使用new关键字调用抽象类的构造器创建抽象类的实例。抽象类不包含抽象方法也不能实例化;抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口和枚举)5成分。抽象类的构造器不能实例化,只能用于被子类调用;含有抽象方法的类(包括直接定义了一个抽象方法,或者继承一个抽象父类但没有完全实现父类包含的抽象方法;实现一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。

abstract class abstractClass{
    {
        System.out.println("abstractClass初始化块");
    }
    String name;
    //抽象方法
    //抽象方法没有方法体,不能用private修饰,抽象方法就是为了重写的
    public abstract void test();
    public abstract void test2(String name);
}
public class AbstractClassSon extends abstractClass{
    public void test(){
        System.out.println("子类重写抽象类不带参数的抽象方法");
    }
    public void test2(String name){
        System.out.println("子类重写抽象类带参数的抽象方法,参数值为: " + name);
    }
    public static void main(String[] args){
        AbstractClassSon acs = new AbstractClassSon();
        acs.test();
        acs.test2("lilei");
    }
}

final不能与abstract同时使用,final修饰的不能被重写,抽象类就是为了重写。

abstract不能修饰成员变量,也不能修饰局部变量,没有抽象变量、抽象成员变量的说法;abstract也不能修饰构造器,没有抽象构造器的说法。

static和abstract不能同时使用,没有静态抽象方法。static修饰的方法是类方法,abstract修饰的方法是要被重写的,当抽象类没有被重写无法用一个不存在的方法调用类方法。但abstract和static不能互斥的,它们可以修饰内部类。

abstract关键字修饰的方法必须被子类重写才有意义,因此abstract修饰的方法不能定义为private访问权限。

抽象类不能创建实例,只能当成父类来创建。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。

接口

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,则可以提炼出一种更加特殊的抽象类-接口,接口中不能含有普通方法,接口中所有方法都是抽象方法。Java8进行了改进,允许定义默认方法,默认方法提供方法的实现。

一个接口可以有多个父接口,但接口只能继承接口不能继承类

interface 接口名 extends 父接口 implements 接口1, 接口2.。。

由于接口定义的是一种规范,所以接口不能包含构造器和初始化块。接口里可以包含成员变量(只能是静态变量)、方法(只能是抽象方法、类方法或默认方法)内部类(内部接口、枚举)

接口中所有成员都是public访问权限,接口中定义的静态常量而言,它们是接口相关的,因此系统会自动为这些成员变量添加static 和final修饰符。接口中定义成员变量系统会自动帮你添加public static final修饰符。接口定义的成员变量只能在定义时指定默认值。

public static final int MAX_SIZE = 36E;

接口定义的方法只能是抽象方法、类方法或默认方法,因此如果不是定义默认方法,系统将自动为普通方法添加abstract修饰符。接口中普通方法不能有方法实现,但类方法、默认方法都必须有方法实现。

interface InterfaceClass{
    //抽象方法
    public abstract void abstractMethod();
    //默认方法
    default void defaultMethod(){
        System.out.println("接口中的默认方法");
    }
    //接口的类方法
    static void staticMethod(){
        System.out.println("接口中的类方法");
    }
}
public class InterfaceTest implements InterfaceClass{
    //冲写接口的抽象方法
    public void abstractMethod(){
        System.out.println("重写接口的抽象方法");
    }
    public static void main(String[] args){
        InterfaceTest it = new InterfaceTest();
        it.abstractMethod();
        it.defaultMethod();
        //staticMethod();
    }
}

接口完全支持多继承

interface ExtendsInterface{
    int i = 10;
    public abstract void father1Method();
}

interface ExtendsInterface2{
    int j = 20;
    public abstract void father2Method();
}

interface InterfaceClass extends ExtendsInterface, ExtendsInterface2{
    //接口可以多继承
}
public class InterfaceTest2 implements InterfaceClass{
    public void father1Method(){
        System.out.println("father1的抽象方法");
    }
    public void father2Method(){
        System.out.println("father2的抽象方法");
    }
    public static void main(String[] args){
        InterfaceTest2 it = new InterfaceTest2();
        System.out.println("i = " + it.i);
        System.out.println("j = " + it.j);
        it.father1Method();
        it.father2Method();
    }
}

一个类使用extends继承,但只能继承一个类;使用implements实现多个接口。实现接口的方法,必须用public访问控制修饰符。

接口不能显式继承任何类,但所有接口类型的引用变量都可以直接赋给Object类型的引用变量。

接口和抽象类共同点:接口和抽象类不能被实例化,他们位于继承树的顶端,用于被其他类实现和继承;接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类必须实现这些方法。

不同点:接口只能包含抽象方法、静态方法、默认方法,抽象类可以是包含普通方法;接口只能定义静态常量,不能定义普通成员变量,抽象类都可以;接口不包含构造器,抽象类可以包含;接口不包含初始化块,抽象类可以;一个类只能有一个直接父类,但可以实现多个接口。

内部类

把一个类放在另一个类的内部定义,成为内部类。

内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许统一包中其他类访问该类;内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员可以互相访问,但外部类不能访问内部类的实现细节;匿名内部类适合用于创建仅需要使用一次的类。

public class outClass{
  //定义内部类  
}

大部分时候,内部类都被作为成员内部类定义,而不是局部内部类。成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。

成员内部类分为两种:静态内部类和非静态内部类,静态内部类使用static修饰

public class InnerClass{
    private String name;
    public InnerClass(String name){
        this.name = name;
    }
    //内部类
    public class NBLClass{
        private int age;
        private String color;
        public NBLClass(){}
        public NBLClass(int age, String color){
            this.age = age;
            this.color = color;
        }
        public void info(){
            System.out.println("age = " + age +  "  color = " + color);
            System.out.println("访问外部类private修饰的成员变量,name的值为" + name);
        }
    }
    public void test(){
        NBLClass nblc = new NBLClass(18, "green");
        nblc.info();
    }
    
    public static void main(String[] args){
        InnerClass ic = new InnerClass("lilei");
        ic.test();
    }
    
}

把这个类的定义放在另一个类的内部,所以就成了一个内部类,可以使用private修饰符修饰这个类

private class NBLClass{
        private int age;
        private String color;
        public NBLClass(){}
        public NBLClass(int age, String color){
            this.age = age;
            this.color = color;
        }
        public void info(){
            System.out.println("age = " + age +  "  color = " + color);
            System.out.println("访问外部类private修饰的成员变量,name的值为" + name);
        }
    }
    public void test(){
        NBLClass nblc = new NBLClass(18, "green");
        nblc.info();
    }

如果外部类成员变量、内部类成员变量与内部类里的方法的局部变量同名,则可通过this、外部类类名.this作为限定区分

public class InnerClass{
    private String name;
    public InnerClass(String name){
        this.name = name;
    }
    //内部类
    private class NBLClass{
        private String name;
        public NBLClass(String name){
            this.name = name;
        }
        public void info(String name){
            System.out.println(name);
            System.out.println(this.name);
            System.out.println(InnerClass.this.name);

        }
    }
    public void test(){
        NBLClass nblc = new NBLClass("内部类的成员变量");
        nblc.info("内部类的局部变量");
    }
    
    public static void main(String[] args){
        InnerClass ic = new InnerClass("外部类的成员变量");
        ic.test();
    }
    
}

外部类要访问非静态内部类的成员,必须显式创建非静态内部类对象类调用访问其实例成员

public class InnerClass{
    private int age = 100;

    //内部类
    private class NBLClass{
        private int InnerAge = 200;
        public void info(){
            System.out.println("外部类age的值: " + age);

        }
    }
    public void test(){
        //外部类访问非静态内部类必须显式创建内部类的实例,再调用变量
        System.out.println("内部类InnerAge的值" + new NBLClass().InnerAge);
    }
    
    public static void main(String[] args){
        InnerClass ic = new InnerClass();
        ic.test();
    }
    
}

非静态内部类对象必须寄生在外部类对象中,而爱不累对象则不必一定有非静态内部类对象寄生其中。如果存在一个非静态内部类对象,则一定存在一个被它寄生的外部类。

Java不允许非静态内部类定义静态成员。非静态内部类不能有静态初始化块,但可以包含普通初始化块。非静态内部类普通初始化块的作用与外部类初始化块的一样。

静态内部类可以包含静态成员,也可以包含非静态成员。根据静态不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。

public InnerStaticClass{
    private static int i = 100;
    
    static class InnerClass{
        private static int j = 200;
        public void test(){
            System.out.println("静态内部类只能访问外部类的静态成员: " + i);
    }
}

静态内部类是外部类的一个静态成员,因此外部类的所有方法,所有初始化块可以使用静态内部类类定义变量、创建对象等。

外部类不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问内部类的类成员,也可以使用静态内部类对象作为调用者访问。

public class InnerStaticClass{
    
    static class InnerClass{
        private static int j = 100;
        private static int i = 200;
    }
    public static void main(String[] args){
        //使用静态内部类类名访问变量
        System.out.println(InnerClass.i);
        //创建静态内部类对象访问变量
        System.out.println(new InnerClass().j);
    }
}

Java还允许接口里定义内部类,皆苦定义的内部类默认使用public static 修饰。也就是接口内部类只能是静态内部类。

在外部类中使用内部类和普通类没什么区别,需要注意的是不能在外部类的静态成员中使用非静态内不来,静态成员不能访问非静态成员。

在外部类以外使用非静态内部类,则内部类不能用private修饰,private修饰只能在外部类中使用。

class InnerClass{
    class In{
        public In(String name){
            System.out.println(name);
        }
    }
}

public class OutClass{
    public static void main(String[] args){
        //外部类以外访问内部类
        //非静态内部类的构造器碧玺使用外部类对象调用
        InnerClass.In icin = new InnerClass().new In("lilei");
    }
}

在外部类以外使用静态内部类

class InnerClass{
    static class In{
        public In(String name){
            System.out.println(name);
        }
    }
}

public class OutClass{
    public static void main(String[] args){
        //外部类以外访问静态内部类
        InnerClass.In icin = new InnerClass.In("lilei");
    }
}

其中new InnerClass.In()这里是类名.静态成员的格式访问静态成员,与非静态内部类创建外部类对象访问的方式不同。

局部内部类仅在该方法中有效,局部内部类不能在外部类的方法以外的地方使用,因此局部内部类也不能使用访问控制符和static修饰符修饰。

public class OutClass{
    public static void main(String[] args){
        
        class In{
            int a = 10;
        }
        //外部类以外访问静态内部类
        In i = new In();
        System.out.println(i.a);
    }
}

匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类或实现一个接口

匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象,因此不允许将匿名内部类定义成抽象类。

匿名内部类不能定义构造器,由于匿名内部类没有类名,无法定义构造器。

interface p{
    
}

public class Test{
    public void method(p s){
        
    }
    
    public static void main(String[] args){
        Test t = new Test();
        t.method(new p(){
            //匿名内部类
        });
    }
}

当通过实现接口来创建匿名内部类时,匿名内部类也不能显式创建构造器,因此匿名内部类只有一个隐式的无参数构造器,故new接口名后的括号不能传入参数值

如果通过继承父类来创建匿名内部类,匿名内部类将拥有和父类相似的构造器,拥有相同的形参列表。

枚举类

使用enum定义枚举类,一个Java源文件最多只能定义一个public访问权限的枚举类,使用enum定义的枚举类默认继承java.lang.enum类,而不是Object类。使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。

 

如果其含有public构造器,那么在类的外部就可以通过这个构造器来新建实例,显然这时实例的数量和值就不固定了,这与定义枚举类的初衷相矛盾,为了避免这种形象,就对枚举类的构造器默认使用private修饰。如果为枚举类的构造器显式指定其它访问控制符,则会编译出错。 另外,注意枚举类的所有实例必须在其首行显式列出,否则它不能产生实例。

 

枚举类的构造器只能使用private修饰,枚举类所有势力必须在枚举类第一行显式列出,否则不能产生实例。

public enum EnumClass1{
    SPRING, SUMMER, FALL, WINTER;
    
    //枚举类的构造器只能使用private修饰
    private EnulClass1(){
        
    }
    public static void main(String[] args){
        System.out.println(EnumClass1.SPRING);
    }
}

 

通过values()方法,返回枚举类的所有实例

enum EnumClass1{
    SPRING, SUMMER, FALL, WINTER;
}
public class Test{
    public static void main(String[] args){
        for (EnumClass1 ec : EnumClass1.values()){
            System.out.println(ec);
        }
    }
}

枚举类也是一种类,只是比较特殊。但也可以定义成员变量、方法和构造器

enum EnumClass1{
    SPRING, SUMMER, FALL, WINTER;
    
    //枚举类定义成员变量
    public String name;
}
public class EnumTest{
    public static void main(String[] args){
        //通过values()方法访问枚举类特定的枚举值
        EnumClass1 ec = EnumClass1.valueOf(EnumClass1.class, "SPRING");
        
        ec.name = "春天";
        System.out.println(ec.name);
        
    }
}
enum EnumClass1{
    SPRING, SUMMER, FALL, WINTER;
    
    //枚举类定义成员变量
    public String name;
}
public class EnumTest{
    public static void main(String[] args){
        //通过values()方法访问枚举类特定的枚举值
        //EnumClass1 ec = EnumClass1.valueOf(EnumClass1.class, "SPRING");
        
        //和上面注释效果是一样的
        EnumClass1.SPRING.name = "春天";
        System.out.println(EnumClass1.SPRING.name);
        
    }
}

getter和setter方法

enum EnumClass1{
    SPRING, SUMMER, FALL, WINTER;
    
    //枚举类定义成员变量
    private String name;
    
    public String getName(){
        return name;
    }
    
    public void setName(String name){
        this.name = name;
    }
}
public class EnumTest{
    public static void main(String[] args){
        //通过values()方法访问枚举类特定的枚举值
        //EnumClass1 ec = EnumClass1.valueOf(EnumClass1.class, "SPRING");
        
        //和上面注释效果是一样的
        EnumClass1.SPRING.setName("春天");
        System.out.println(EnumClass1.SPRING.getName());
        
    }
}

枚举类的实例只能是枚举值,不能new。

枚举类通常设计成不可变类,也就是成员变量值不应该允许改变,这样更安全。建议枚举类成员边浪使用private final修饰。

枚举类实例可以重写方法

enum EnumClass1{
    SPRING{
        public void setName(String name){
            this.name = name;
            System.out.println("重写了方法");
        }
    }, SUMMER, FALL, WINTER;
    
    //枚举类定义成员变量
    public String name;
    
    public String getName(){
        return name;
    }
    
    public void setName(String name){
        this.name = name;
    }
}
public class EnumTest{
    public static void main(String[] args){
        //通过values()方法访问枚举类特定的枚举值
        //EnumClass1 ec = EnumClass1.valueOf(EnumClass1.class, "SPRING");
        
        //和上面注释效果是一样的
        EnumClass1.SPRING.setName("春天");
        System.out.println(EnumClass1.SPRING.getName());
        
    }

枚举类可以实现接口

interface interfaceTest{
    void method();
}

public enum EnumClass2 implements interfaceTest{
    SPRING, SUMMER, FALL, WINTER;
    
    public void method(){
        System.out.println("枚举类实现接口");
    }
}

不同的枚举值实现接口有不同的表现

interface interfaceTest{
    void method();
}

public enum EnumClass2 implements interfaceTest{
    //这里的枚举值是根据构造器创建的
    SPRING("春天"), SUMMER("夏天"), FALL("秋天"), WINTER("冬天");
    
    public String name;
    private EnumClass2(String name){
        this.name = name;
    }
    public void method(){
        System.out.println("枚举类实现接口");
    }
    public static void main(String[] args){
        System.out.println(SPRING.name); //输出春天
    }
}

有抽象方法的枚举类

public enum EnumClass3{
    SPRING{
        public void method(){
            System.out.println("春天重写抽象方法");
        }
    }, SUMMER{
        public void method(){
            System.out.println("夏天重写抽象方法");
        }
    };
    
    public abstract void method();
    
    public static void main(String[] args){
        SPRING.method();
        SUMMER.method();
    }
}

在枚举类中使用匿名内部类是会产生匿名内部子类的,非抽象的枚举内部类才默认使用final修饰。

 对象与垃圾回收

垃圾回收机制只负责回收堆内存的对象,不会回收任何物理资源。

程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行。当对象永久性失去引用后,系统就会在合适的时候回收它的内存。

在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法,该方法可能使该对象重新复活,从而导致垃圾回收机制取消。

当一个对象在对内存运行时,根据它被引用变量所引用的状态,可以分为三种:

  可达状态,当一个对象创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的实例变量和方法。

  可恢复状态,如果程序中某个对象不再有任何引用变量引用它,就进入可恢复状态。在这种状态下,系统的垃圾回收机制准备回收该对象所占的内存,在回收该对象之前,系统会调用所有可恢复对象的finalize()方法进行资源清理。

  不可达状态,当对象与所有引用变量的关联都被切断,且系统已经调用所有的对象finalize()方法后依然没有使用该对象变成可达状态,这个对象将永久性失去引用。

系统无法精确控制Java垃圾回收机制,但依然可以强制系统进行垃圾回收,这种强制只是通知系统进行垃圾回收,但系统啥时候来依然未知。

强制垃圾回收机制方式:1、System.gc()静态方法;2、Runtime对象的gc方法,Runtime.getRuntime().gc();这种强制机制只是建议系统进行垃圾回收,系统完全有可能并不立即进行垃圾回收,垃圾回收机制也不会对程序的建议完全置之不理,垃圾回收机制会在收到通知以后尽快进行垃圾回收。

当finalize()方法返回以后,对象消失垃圾回收机制开始执行。

某个失去引用的对象占有极小的内存,系统没有严重的内存需求,因此垃圾回收机制并没有试图回收该对象所占有的内存,该对象的finalize()方法不会得到调用。

finalize()方法特点:1、永远不要主动调用某个对线的fianlize()方法,该方法应交给垃圾回收机制调用; 2、fianlize()方法何时被调用,是否被调用具有不确定性,不要把finalize()方法当做一定会执行的方法; 3、当JVM执行可恢复对象的finalize()方法时,可能使该对象或系统中其他对象重新变成可达状态; 4、当JVM执行finalize()方法出现异常时,垃圾回收机制不会报错,程序会继续执行。

 

posted on 2019-06-17 08:09  在劫  阅读(447)  评论(0)    收藏  举报