Java常见面试题

来源:互联网 发布:网络危机公关联系方式 编辑:程序博客网 时间:2024/04/28 16:05

面向对象都有哪些特性以及你对这些特性的理解

继承
继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。

封装
通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。

多态性
多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A 系统访问B 系统提供的服务时,B 系统有多种提供服务的方式,但一切对A 系统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要
做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型
对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

抽象
抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么

java中实现多态的机制是什么?

靠的是父类或接口定义的引用变量指向子类或具体实现类的实例对象,而程序调用的方法在运行期间才动态绑定,就是引用变量指向的具体实例对象的方法,也就是在内存里面正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

例如:父类A有一个方法function(),子类B,C分别继承A并且重写function(),当创建一个对象A b = new B(); b.function()就调用B的funciotn,假如你A c = new C(),那调用的就是C重写的function。怎么判断使用那个类的function就是动态绑定,这个现象就是多态

重写、重载是多态性的不同表现

3、抽象类和接口的区别

4、调用下面的方法,得到的返回值是什么

public int getNum() {        try {            int a = 1 / 0;            return 1;        } catch (Exception e) {            return 2;        } finally {            //int b = 1/0;            return 3;        }    }

代码在走到第3 行的时候雨大了一个MathException,这时第四行的代码就不会执行了,代码直接跳转到catch 语句中,走到第6 行的时候,异常机制有这么一个原则如果在catch 中遇到了return 或者异常等能使该函数终止的话那么用finally 就必须先执行完finally 代码块里面的代码然后再返回值。因此代码又跳到第8
行,可惜第8 行是一个return 语句,那么这个时候方法就结束了,因此第6 行的返回结果就无法被真正返回。如果finally 仅仅是处理了一个释放资源的操作,那么该道题最终返回的结果就是2,因此上面返回值是3。

序列化

  • Serializable
    在java 中能够被序列化的类必须先实现Serializable 接口,该接口没有任何抽象方法只是起到一个标记作用
 //对象输出流ObjectOutputStream objectOutputStream = new ObjectOutputStream(new        FileOutputStream(new File("D://obj")));objectOutputStream.writeObject(new User("zhangsan", 100));objectOutputStream.close();//对象输入流ObjectInputStream objectInputStream = new ObjectInputStream(new        FileInputStream(new File("D://obj")));User user = (User)objectInputStream.readObject();System.out.println(user);objectInputStream.close();
  • Parcelable
public class MyParcelable implements Parcelable {     private int mData;     public int describeContents() {         return 0;     }     public void writeToParcel(Parcel out, int flags) {         out.writeInt(mData);     }     public static final Parcelable.Creator<MyParcelable> CREATOR             = new Parcelable.Creator<MyParcelable>() {         public MyParcelable createFromParcel(Parcel in) {             return new MyParcelable(in);         }         public MyParcelable[] newArray(int size) {             return new MyParcelable[size];         }     };     private MyParcelable(Parcel in) {         mData = in.readInt();     } }

synchronized 和volatile 关键字的作用

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile 修饰之后,那么就具备了两层语义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是
    立即可见的。
  • 禁止进行指令重排序

volatile 本质是在告诉jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;

synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

  • volatile 仅能使用在变量级别;
    synchronized 则可以使用在变量、方法、和类级别的

  • volatile 仅能实现变量的修改可见性,并不能保证原子性;
    synchronized 则可以保证变量的修改可见性和原子性

  • volatile 不会造成线程的阻塞;
    synchronized 可能会造成线程的阻塞。

  • volatile 标记的变量不会被编译器优化
    synchronized 标记的变量可以被编译器优化

JVM垃圾回收

理论上来讲Sun 公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法
也不尽相同。

GC(Garbage Collector)在回收对象前首先必须发现那些无用的对象,如何去发现定位这些无用的对象?常用
的搜索算法如下:

  • 引用计数器算法(废弃)
  • 根搜索算法(使用)
  • 复制算法(Copying)

JVM内存结构和内存分配

1、Java 内存模型

Java 虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java 栈和Java 堆

  • 方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会在运行时改变。
    常数池,源代码中的命名常量、String 常量和static 变量保存在方法区。

  • Java Stack 是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。最典型的Stack 应用是方法的调用,Java 虚拟机每调用一次方法就创建一个方法帧(frame),退出该方法则对应的方法帧被弹出(pop)。栈中存储的数据也是运行时确定的。

  • Java 堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java 对象的内存总是在heap 中分配。我们每天都在写代码,每天都在使用JVM 的内存。

2、java 内存分配

  • 基本数据类型直接在栈空间分配

  • 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收

  • 引用数据类型,需要用new 来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量

  • 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收

  • 局部变量new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC 回收

  • 方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放

  • 字符串常量在DATA 区域分配,this 在堆空间分配

  • 数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小

Java 中引用类型都有哪些

Java 中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用

  • 强引用(StrongReference)

这个就不多说,我们写代码天天在用的就是强引用。如果一个对象被被人拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

Java 的对象是位于heap 中的,heap 中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下代码:

String abc=new String("abc"); //1SoftReference<String> softRef=new SoftReference<String>(abc); //2WeakReference<String> weakRef = new WeakReference<String>(abc); //3abc=null; //4softRef.clear();//5

第一行在heap 堆中创建内容为“abc”的对象,并建立abc 到该对象的强引用,该对象是强可及的。

第二行和第三行分别建立对heap 中对象的软引用和弱引用,此时heap 中的abc 对象已经有3 个引用,显然此时abc 对象仍是强可及的。

第四行之后heap 中对象不再是强可及的,变成软可及的。

第五行执行之后变成弱可及的。

  • 软引用(SoftReference)

如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

软引用是主要用于内存敏感的高速缓存。在jvm 报告内存不足之前会清除所有的软引用,这样以来gc 就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc 的算法和gc 运行

时可用内存的大小。当gc 决定要收集软引用时执行以下过程,以上面的softRef 为例:

  1. 首先将softRef 的referent(abc)设置为null,不再引用heap 中的new String(“abc”)对象。
  2. 将heap 中的new String(“abc”)对象设置为可结束的(finalizable)。
  3. 当heap 中的new String(“abc”)对象的finalize()方法被运行而且该对象占用的内存被释放, softRef被添加到它的ReferenceQueue(如果有的话)中

注意:对ReferenceQueue 软引用和弱引用可以有可无,但是虚引用必须有

被Soft Reference 指到的对象,即使没有任何DirectReference,也不会被清除。一直要到JVM 内存不足且没有Direct Reference 时才会清除,SoftReference 是用来设计object-cache 之用的。如此一来SoftReference 不但可以把对象cache 起来,也不会造成内存不足的错误(OutOfMemoryError)

  • 弱引用(WeakReference)

如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被gc 扫描到了随时都会把它干掉

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中

虚引用(PhantomReference)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动

虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关

联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

建立虚引用之后通过get 方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get 方法返回结果为null。先看一下和gc 交互的过程再说一下他的作用

1、不把referent 设置为null, 直接把heap 中的new String(“abc”)对象设置为可结束的(finalizable)

2、与软引用和弱引用不同, 先把PhantomRefrence 对象添加到它的ReferenceQueue 中.然后在释放虚可及的对象

0 0