资讯专栏INFORMATION COLUMN

java面向对象(下)

awesome23 / 3079人阅读

内部类

内部类主要作用

内部类提供了更好的封装,可以吧内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问,但外部类不能访问内部类的实现细节。

匿名内部类适合用于创建那些仅需要使用一次的类。

定义内部类与外部类的语法大致相同,内部类除需要定义在其他类里面之外,还有两点区别:

内部类比外部类可以多使用三个修饰符:private protected static - 外部类不可以使用这三个修饰符。

非静态内部类不能拥有静态成员。

外部类上一级程序单元是包,所有他只有俩个作用域:同一个包和任何位置,public 和包默认访问权限。而内部类的上一级程序单元是外部类,他就有四个作用域:同一个类,同一个包,父子类和任何位置。

在外部类里使用非静态内部类时,与平时使用普通类并没有太大的区别。

非静态内部类可以直接访问其外部类的private 实例变量,因为在非静态内部类对象里,保存了一个它所寄生的外部类对象的引用,当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里。

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

非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须创建非静态内部类对象来调用访问其实例成员。

非静态内部类对象和外部类对象的关系是什么样的?

非静态内部类对象必须计生在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄生其中。简单来说,如果存在一个非静态内部类对象,则一定存在一个被他寄生的外部类对象。但外部类对象存在时,外部类对象里不一定寄生了非静态内部类对象。因此外部类对象访问非静态内部类成员时,可能非静态普通内部类对象根本不存在,而非静态内部类对象访问外部类成员时,外部类地向一定存在。

根据静态成员不能访问非静态成员的规则,外部类的静态方法,静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量,创建实例等。不允许在外部类的静态成员中使用非静态内部类。

Java不允许在非静态内部类里定义静态成员。

非静态内部类里不能有静态方法,静态成员变量,静态初始化块。

静态内部类就是用static修饰的内部类。

Static的作用是把类的成员变成类相关。而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。

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

为什么静态内部类的实例方法也不能访问外部类的实例属性呢?

因为静态内部类时外部类的类相关的,而不是外部类的对象相关的。也就是说静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的类本身中。当静态内部类对象存在时,并不存在一个被他寄生的外部类对象。静态内部类对象只持有外部类的引用,没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类的对象,将产生错误。

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

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

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

在外部类内部使用内部类

在外部类内部使用内部类时,与平常使用普通类没有太大的区别。一样可以直接通过内部类类名来定义变量,通过new调用内部类构造器来创建实例。

区别不要在外部类的静态成员中使用非静态内部类,因为静态成员不能访问非静态成员。

在外部类内部定义内部类的子类与平常定义子类也没有太大的区别。

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

在外部类以外的地方访问内部类包括静态和非静态,则内部类不能使用private访问修饰符,private修饰的内部类只能在外部类内部使用。对于使用其他的访问控制修饰符的说明:

省略访问控制修饰符的内部类,只能被与外部类处于同一包下的其他类所访问。

使用protected修饰的内部类,可被与外部类处于同一包中的其他类和外部类的子类所访问。

Public修饰的内部类,可以在任何地方被访问。

在外部类以外的地方定义内部类语法格式:OutClasss.InnerClass varName

在外部类以外的地方创建非静态内部类实例的语法:OutClassInstance.new InnerConstructor()。

非静态内部类的构造器必须使用外部类对象来调用。

非静态内部类的构造器必须通过其外部类对象来调用。

当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。

-代码实例-
public class Out {

public Out() {
    System.out.println("Out 构造");
}
class In{
    public In(String name) {
        System.out.println("In 构造 " + name);
    }
}

}
public class SubClass extends Out.In{

public SubClass(Out out, String name) {
    out.super(name);
    System.out.println("SubClass 构造");
}
public static void main(String[] args) {
    new SubClass(new Out(), "NAME ");  //创建本类对象

// new Out().new In("d"); 创建In的对象

}

}
Output:
Out 构造
In 构造 NAME
SubClass 构造

如上SubClass的构造器中out.super(“name”)中的super代表的是In内部类的构造器。想创建非静态内部类的子类,首先需要创建非静态内部类。但是非静态内部类的创建必须依赖一个外部类对象,所以需要一个OUT类的对象。如果是静态内部类的子类,那么就只需要用super 调用静态内部类构造器创建子类对象即可。

非静态内部类的子类不一定是内部类,他可以是一个外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象。也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。

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

在外部类以外的地方创建静态内部类实例语法new OutClass.InConstructor();

不管是静态内部类还是非静态内部类,他们声明变量的语法完全一样。区别就是在创建内部类对象时,静态内部类只需使用外部类即可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。

如果把一个局部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效,由于局部内部类不能在外部类的方法以外的地方使用,因此局部内部类也不能使用访问控制修饰符和static修饰。

匿名内部类适合创建于只需要一次使用的类。

匿名内部类规则

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

匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义初始化块,可以通过实例初始化来完成构造器需要完成的事情。

由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。

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

当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,可以重写父类中的普通方法。

Java 8 更加智能:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰。

被匿名内部类访问的局部变量必须使用final修饰。

Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口,函数接口可以包含多个默认方法,类方法,但只能声明一个抽象方法。。

Lambda表达式与匿名内部类的主要区别

匿名内部类可以为任意接口创建实例,不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可,但Lambda表达式只能为函数式接口创建实例。

匿名内部类可以为抽象类甚至普通类创建实例,但Lambda只能为函数式接口创建实例。

匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda的代码块不允许调用接口中定义的默认方法。

枚举类

枚举类是一种特殊的类,他一样可以有自己的成员变量和方法。可以实现一个或者多个接口,也可以定义自己的构造器。

枚举类和普通类的区别

枚举类可以实现一个或者多个接口,默认继承Enum类,而不是默认继承Object类 ,因此枚举类不能显示的继承其他父类。

使用enum定义,非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。

枚举类的构造器只能使用private修饰,

枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。系统会为其默认添加public static final修饰。

定义枚举类时,需要显示列出所有的枚举值,以逗号隔开,枚举值列列举借书后以分号作为结尾。

枚举类的实例只能是枚举值,

一旦为枚举类显示定义了带参数的构造器,那么枚举值就必须对应的传入参数。

获取枚举类的枚举的值可以使用getName方法。

使用implements实现接口,并实现接口里包含的抽象方法。

枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义为抽象类,因为系统会自动添加,但因为枚举类需要显示创建枚举值,而不是作为父类,所以定义的每个枚举值都必须为抽象方法提供实现。

当程序创建对象,数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保留在这块内存区中,当这块内存不再被任何引用变量引用时,这块内存就变成了垃圾,等待垃圾回收机制回收。

垃圾回收机制

垃圾回收机制的特征:

垃圾回收机制只负责堆内存中的对象,不会回收任何物力资源,如数据库联建。网络IO

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

在垃圾回收机制回收任何对象之前,总会先调用他的finalize方法,该方法可能使该对象重新复活,让一个引用变量重新引用该对象,从而导致垃圾回收机制取消回收。

对象在内存中的状态

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

可恢复状态:如果程序中某个对象不再有任何引用变量引用他,他就进入了可恢复状态,在这种状态下,系统的垃圾回收机制准备回收该对象之前,总会调用finalize方法进行资源清理,如果方法执行后重新让一个引用变量指向了该对象,那么此对象变成可达对象,否则进入不可达状态。

不可达状态。当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的finalize方法后依然没有使该对象变成可达状态,那么这个对象将永久的失去引用,最后变成不可达状态,只有当一个对象处于不可达状态时,系统才会真正的回收此对象所占的资源。

public class Finalizes {
    public static Finalizes finalizes = null;
    public static void main(String[] args) {
        new Finalizes();
        System.gc();//建议垃圾回收    1
        System.runFinalization();//强制调用finalize方法。     2 
        finalizes.info();//并不会发生空指针异常
    }
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize -----");//重写finalize方法
        //这里的this意思是,系统会调用所有对象的finalize方法,那么此this就是系统调用的对象。
        //由于在上面的main的方法中创建了一个新的对象,但是此对象并没有指向任何引用变量,此刻他是可恢复状态,当调用了此对象的
        //finalize方法后,系统就会将堆内存中的此对象赋值给类变量finalizes,此刻finalizes已经拥有的在堆内存中的对象。
        finalizes = this;
    }
    private  void info() {
        System.out.println("info");
    }
}

如上代码,如果取消了1处代码,程序并没有通知系统开始执行垃圾回收,而且程序内存也没有紧张、因此系统通常不会立即进行垃圾回收,也就不回调用创建对象的finalize方法,这样类变量依旧保持为null,引发空指针。如果取消掉2处代码,由于JVM垃圾回收机制的不确定性,JVM往往并立刻调用可恢复的finalize方法,这样类变量依旧为null。依旧为空指针异常。

Java语言对对象的引用

Java语言对对象的引用如下:

强引用StrongReference

程序创建一个对象,并把对象赋给一个引用变量,程序通过该引用变量来操作实际的对象,当一个对象被一个或一个以上的引用变量所引用时,处于可达状态,不可能被系统垃圾回收机制回收,

软引用SoftReference

当一个对象只有软引用时,他有可能被垃圾回收机制回收,对于只有软引用的对象而言,当系统内存空间足够时,他不会被系统回收,程序也可使用该对象;当系统内存不足,系统可能会回收他,软引用通常用于对内存敏感的程序中。

弱引用WeakReference

弱引用与软引用相似,但是比软引用级别更低,对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。

虚引用PhantomReference

虚引用完全类似没有引用,虚引用对对象本身没有太大影响,虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能多带带使用,虚引用必须和引用队列ReferenceQueue联合使用。

引用队列用java.lang.ReferenceQueue表示,它用于保存被回收后对象的引用。当联合使用软引用,弱引用和引用队列时,系统在回收被引用对象之后,将把被回收对象对应的引用添加到关联的引用队列中。

与软引用和虚引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到他关联的引用队列中,这使得可以在对象被回收之前采取行动。

软引用和弱引用可以多带带使用,但虚引用不能。

虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用的对象是否即将被回收。

-如下弱引用代码实例:-

public class ReferenceT {
    public static void main(String[] args) {
        String string = new String("YYYYY");
        WeakReference weakReference = new WeakReference(string);//弱引用引用字符串YYYYY
        System.out.println(weakReference.get());//得到弱引用关联的string对象的值
        string = null;//将string引用对象与堆内存中的对象断开引用关系
        System.out.println(weakReference.get());//依旧可以输出
        System.gc();//提示进行垃圾清理
        System.runFinalization();//强制执行finalize方法
        System.out.println(weakReference.get());//finalize方法执行后回收垃圾对象
    }
}

采用String string =”String”时,代码定义字符串时,系统会使用常量池来管理这个字符串直接量,会使用强引用来引用他,系统就不会回收此字符串直接量了。

-下列虚引用代码,虚引用无法获取他所引用的对象。-

public class PhantomRef {
    public static void main(String[] args) {
        String str = new String("啦啦");
        //创建队列
        ReferenceQueue rq = new ReferenceQueue();
        //创建虚引用与队列和对象关联
        PhantomReference pr = new PhantomReference(str, rq);
        str = null;
        //虚引用不能取出关联对象中的值 null
        System.out.println(pr.get()+" get");
        System.gc();
        System.runFinalization();
        //垃圾清理之后,许引用将被放入引用队列中
        //true
        System.out.println(rq.poll() == pr);
    }
}

使用这些引用类可以避免在程序执行期间将对象保留在内存中,如果以软引用,弱引用,或虚引用的方式引用对象,垃圾回收期就能够随意的释放对象。

要使用这些特殊的引用类,就不能保留对对象的强引用,如果保留了对对象的强引用,就会浪费这些引用类所提供的任何好处。

修饰符strictfp含义是FP-strict,就是精确浮点的意思。一旦使用了strictfp来修饰类,方法和接口时,那么在所修饰的范围内java的编译器和运行时环境会完全安装浮点规范来执行IEEE-754.

Native修饰符主要用于修饰方法,native方法通常使用C语言来实现,如果某个方法需要利用平台相关特性,或者访问系统硬件等,那就可以使用native修饰该方法,再交由C去实现。一旦包含native方法,这个程序将失去跨平台性。

Abstract和final永远不能同时使用,abstract和static不能同时修饰一个方法,可以同时修饰内部类。Abstract和private不能同时修饰方法,可以同时修饰内部类。Private和final同时修饰方法虽然语言是合法的,但是没有必要,因为private修饰的方法不可能被子类重写。因此使用final修饰没意义。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69290.html

相关文章

  • 谈谈我所理解的面向对象

    摘要:众多面向对象的编程思想虽不尽一致,但是无论哪种面向对象编程语言都具有以下的共通功能。原型编程以类为中心的传统面向对象编程,是以类为基础生成新对象。而原型模式的面向对象编程语言没有类这样一个概念。 什么是面向对象?这个问题往往会问到刚毕业的新手or实习生上,也是往往作为一个技术面试的开头题。在这里我们不去谈如何答(fu)好(yan)问(guo)题(qu),仅谈谈我所理解的面向对象。 从历...

    avwu 评论0 收藏0
  • 7. 初步理解面向对象 【连载 7】

    摘要:是一种典型的面向对象编程语言。这篇文章主要是来初步理解一下面向对象的思维为下面的内容先给一个基础。针对面向对象编程的更多内容,会在后面的文章里面详细解释。他们都称之为对象。之后,我们再用编程语言,把这种映射编写出来,就是的面向对象编程啦。 showImg(https://segmentfault.com/img/remote/1460000012983458?w=900&h=500);...

    keelii 评论0 收藏0
  • Java编程需要注意的地方

    摘要:学编程真的不是一件容易的事不管你多喜欢或是多会编程,在学习和解决问题上总会碰到障碍。熟练掌握核心内容,特别是和多线程初步具备面向对象设计和编程的能力掌握基本的优化策略。   学Java编程真的不是一件容易的事,不管你多喜欢或是多会Java编程,在学习和解决问题上总会碰到障碍。工作的时间越久就越能明白这个道理。不过这倒是一个让人进步的机会,因为你要一直不断的学习才能很好的解决你面前的难题...

    leanxi 评论0 收藏0
  • Java面试题:面向对象,类加载器,JDBC, Spring 基础概念

    摘要:为什么不是面向对象不是面向对象,因为它包含个原始数据类型,例如。自定义类加载器继承的自定义类加载器。可以像下面这样指定参数面试题面向对象,类加载器,基础概念它们的关系如下启动类加载器,实现,没有父类。自定义类加载器,父类加载器为。 1. 为什么说Java是一门平台无关语言? 平台无关实际的含义是一次编写到处运行。Java 能够做到是因为它的字节码(byte code)可以运行在任何操作...

    Euphoria 评论0 收藏0
  • Java面试题:面向对象,类加载器,JDBC, Spring 基础概念

    摘要:为什么不是面向对象不是面向对象,因为它包含个原始数据类型,例如。自定义类加载器继承的自定义类加载器。可以像下面这样指定参数面试题面向对象,类加载器,基础概念它们的关系如下启动类加载器,实现,没有父类。自定义类加载器,父类加载器为。 1. 为什么说Java是一门平台无关语言? 平台无关实际的含义是一次编写到处运行。Java 能够做到是因为它的字节码(byte code)可以运行在任何操作...

    longmon 评论0 收藏0

发表评论

0条评论

awesome23

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<