面试笔记--Java基础

#面试笔记

Java基础

什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。
Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。
Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

面向对象和面向过程的区别

  • 面向过程
    • 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
    • 缺点:没有面向对象易维护、易复用、易扩展
  • 面向对象
    • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
    • 缺点:性能比面向过程低

Java中的基本类型及其封装类,什么是自动拆装箱

  • 八种基本类型:int,double,long,float,short,byte,char,boolean
  • 对应的封装类型:Integer,Double,Long,Float,Short,Byte,Character,Boolean
  • 自动拆装箱:自动装箱就是将原始类型自动的转换为对应的对象,而拆箱就是将对象类型转换为基本类型。

static关键字是什么意思

  • static方法:静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法 (就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。
  • static变量:声明为static的变量实质上就是全局变量。当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量。静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间
  • static类:通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。

final修饰的特点

final关键字是一个修饰符,可以修饰类、方法和变量。

  1. 对于类:被final修饰的类是一个最终类,不可以被继承。
  2. 对于方法:被final修饰的方法是一个最终方法,不可以被覆盖。
  3. 对于变量: 被final修饰的变量是一个常量,只能被赋值一次。

Java支持多继承么?如果不支持,如何实现?

在java中是单继承的,也就是说一个类只能继承一个父类。
java中实现多继承有两种方式,一是接口,而是内部类.

什么是值传递和引用传递?java中是值传递还是引用传递,还是都有?

  • 值传递 就是在方法调用的时候,实参是将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不影响原来实参
  • 值传递 就是在方法调用的时候,实参是将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不影响原来实参

构造器(constructor)是否可被重写(override)

构造方法是不能被子类重写的,但是构造方法可以重载,也就是说一个类可以有多个构造方法。

hashCode和equals方法的关系

equals相等,hashcode必相等;hashcode相等,equals可能不相等。
重写equals方法时请必须重写hashcode,以保证equals方法相等时两个对象hashcode返回相同的值。

equals与==的区别

==与equals的主要区别是:==常用于比较原生类型,而equals()方法用于检查对象的相等性。另一个不同的点是:如果==和equals()用于比较对象,当两个引用地址相同,==返回true。而equals()可以返回true或者false主要取决于重写实现。

  • 使用==比较有两种情况:
    比较基础数据类型(Java中基础数据类型包括八中:short,int,long,float,double,char,byte,boolen):这种情况下,==比较的是他们的值是否相等。
    引用间的比较:在这种情况下,==比较的是他们在内存中的地址,也就是说,除非引用指向的是同一个new出来的对象,此时他们使用 == 去比较得到true,否则,得到false。
  • 使用equals进行比较:
    equals追根溯源,是Object类中的一个方法,在该类中,equals的实现也仅仅只是比较两个对象的内存地址是否相等,但在一些子类中,如:String、Integer 等,该方法将被重写。

Java面向对象的三个特征与含义

  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
  • 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
  • 多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。

Override和Overload的含义与区别

  • Overload:顾名思义,就是Over(重新)——load(加载),所以中文名称是重载。它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、类型不能相同;或者说可以改变参数、类型但是函数名字依然不变。
  • Override:就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。

Java静态方法可不可以调用非静态方法,为什么?从JVM方面答

不可以。
从java的内存机制去分析,首先当你New 一个对象的时候,并不是先在堆中为对象开辟内存空间,而是先将类中的静态方法(带有static修饰的静态函数)的代码加载到一个叫做方法区的地方,然后再在堆内存中创建对象。所以说静态方法会随着类的加载而被加载。当你new一个对象时,该对象存在于对内存中,this关键字一般指该对象,但是如果没有new对象,而是通过类名调用该类的静态方法也可以。

程序最终都是在内存中执行,变量只有在内存中占有一席之地时才会被访问,类的静态成员(变态和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问,非静态成员(变量和方法)属于类的对象,所以只有在类的对象禅师(创建实例)的时候才会分配内存,然后通过类的对象去访问。

在一个类的静态成员中去访问非静态成员之所以会出错是因为在类的非静态成员不存在的时候静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

那类是什么时候被加载呢?在需要调用的时候被加载。

接口和抽象类的区别

不同点在于:

  1. 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
  2. 类可以实现很多个接口,但是只能继承一个抽象类
  3. 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
  4. 抽象类可以在不提供接口方法实现的情况下实现接口。
  5. Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
  6. Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private,protected 或者是 public 。
  7. 接口是绝对抽象的,不可以被实例化(Java 8已支持在接口中实现默认的方法)。抽象类也不可以被实例化,但是,如果它包含 main 方法的话是可以被调用的。

Object基类有哪些公用方法

  • clone方法:保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常
  • equals方法:在Object中与==是一样的,子类一般需要重写该方法
  • hashCode方法:该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到
  • getClass方法:final方法,获得运行时类型
  • notify方法:唤醒在该对象上等待的某个线程
  • notifyAll方法:唤醒在该对象上等待的所有线程
  • toString方法:转换成字符串,一般子类都有重写,否则打印句柄
  • finalize方法:该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
  • wait方法:使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
    调用该方法后当前线程进入睡眠状态,直到以下事件发生:
    1. 其他线程调用了该对象的notify方法
    2. 其他线程调用了该对象的notifyAll方法
    3. 其他线程调用了interrupt中断该线程
    4. 时间间隔到了,此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常

Java的四种引用,强弱软虚,用到的场景

JDK1.2之前只有强引用,其他几种引用都是在JDK1.2之后引入的.

  • 强引用(Strong Reference) 最常用的引用类型,如Object obj = new Object(); 。只要强引用存在则GC时则必定不被回收。
  • 软引用(Soft Reference) 用于描述还有用但非必须的对象,当堆将发生OOM(Out Of Memory)时则会回收软引用所指向的内存空间,若回收后依然空间不足才会抛出OOM。一般用于实现内存敏感的高速缓存。 当真正对象被标记finalizable以及的finalize()方法调用之后并且内存已经清理, 那么如果SoftReference object还存在就被加入到它的 ReferenceQueue.只有前面几步完成后,Soft Reference和Weak Reference的get方法才会返回null
  • 弱引用(Weak Reference) 发生GC时必定回收弱引用指向的内存空间。 和软引用加入队列的时机相同
  • 虚引用(Phantom Reference) 又称为幽灵引用或幻影引用,虚引用既不会影响对象的生命周期,也无法通过虚引用来获取对象实例,仅用于在发生GC时接收一个系统通知。 当一个对象的finalize方法已经被调用了之后,这个对象的幽灵引用会被加入到队列中。通过检查该队列里面的内容就知道一个对象是不是已经准备要被回收了. 虚引用和软引用和弱引用都不同,它会在内存没有清理的时候被加入引用队列.虚引用的建立必须要传入引用队列,其他可以没有

Hashcode的作用

以Java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:

  1. new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
  2. 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

两个对象用equals方法比较为true,它们的Hashcode值相同吗?

不一定相同。正常情况下,因为equals()方法比较的就是对象在内存中的值,如果值相同,那么Hashcode值也应该相同。但是如果不重写hashcode方法,就会出现不相等的情况。

String、StringBuffer与StringBuilder的区别

  • 可变性:
    String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。
    StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
  • 线程安全性:
    String中的对象是不可变的,也就可以理解为常量,线程安全。
    AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
  • 性能:
    每次对String类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

try catch finally,try里有return,finally还执行么

会执行,在方法 返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是纪录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰

什么是泛型、为什么要使用以及泛型擦除

泛型,即“参数化类型”。
创建集合时就指定集合元素的类型,该集合只能保存其指定类型的元素,避免使用强制类型转换。
Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。 泛型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
类型擦除的主要过程如下:

  1. 将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
  2. 移除所有的类型参数。

Java中哪些类是线程安全的

  • vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
  • statck:堆栈类,先进后出
  • hashtable:就比hashmap多了个线程安全
  • enumeration:枚举,相当于迭代器
  • java.util.current包和java.util.current.atomic包下的类
  • StringBuffer