当先锋百科网

首页 1 2 3 4 5 6 7

Java面试基础

我只是一个练习搬砖的搬运工。。。

1、Java基础

(1)面向对象的特征:继承封装和多态

①继承:

继承实现了IS-A关系,例如Cat和Animal就是一种IS-A关系,因此Cat可以继承Animal,从而获得Animal非private的属性和方法。
Cat可以当做Animal来使用,也就是说可以使用Animal引用Cat对象。父类引用指向对象称为向上转型。

   Animal animal=new Cat()

继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

②封装:

利用抽象数据类型将数据和基于数据的操作封装在一起,使其能构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内容的细节,但可以通过对象对外提供的接口来访问该对象。
封装有三大好处:减少耦合;隐藏内部细节,因此内部结构可以自由修改;可以对成员进行更精确的控制。

③多态:

多态分为编译时多态和运行时多态。编译时多态主要指方法的多态,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
运行时多态有三个条件:继承;覆盖;向上转型。

(2)final、finally和finalize的区别

final:修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承,因此一个类不能既被声明为abstract的,又被声明为final。将变量和方法声明为final变量的,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可以被修改。被声明为final的方法也同样只能使用不能重载。

finall:在异常处理时提供finally块来执行任何清除操作。如果抛出一个异常,那么匹配的catch子句就会执行,然后控制就会进入finally块(如果有的话)。如果同时有多个return,会先执行完finally语句中的代码,再执行finally中的return。

finalize:方法名。Java技术允许finalize()方法在垃圾收集器将对象从内存中清除出去之前做好必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用的情况下对这个对象的调用。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以收集整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象的调用。

(3)Exception、Error、运行时异常与一般异常有何异同

Throwable可以用来表示任何可以作为异常抛出的类,分为两种:Error和Exception。其中Error用来表示JVM无法处理的错误。
异常类Exception又分为运行时异常和非运行时异常。Java异常又可以分为不受检查异常和检查异常。
Error:Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Exception:在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingRescourceException(丢失异常)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以选择不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序的角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

(4)写出五种常见的runtime Exception

NullPointerException 空指针异常
ClassCastException 类型强制转换异常
IllegalArgumentException 算术异常
IndexOutOfBoundsException 下标越界异常
ArrayStoreException 向数组中存放与声明类型不兼容对象异常
等。。。。

(5)int和Integer区别,Integer的值缓存范围

int是基本数据类型,直接存数值,进行初始化时int类的变量初始为0。
Integer是对象,用一个引用指向这个对象,Integer的变量则初始化为null。
给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf
JVM会自动维护八种基本的常量池,int常量池中初始化-128~127的范围,所以当为Integer I=127 时,在自动装箱过程中是取自常量池中的数值,而当Integer i=128时,128不在常量池范围类,所以在自动装箱过程中需new 128,所以地址不一样。
①当数值范围为-129~127时:如果两个new出来Integer对象,即使值相同,通过“==”比较结果为false,但两个对象直接赋值,则通过“==”比较结果为true,这一点与String非常相似。
②当数值不在-128~127时,无论通过哪种方式,即使两个对象的值相等,通过“==”比较,结果为false。
③当一个Integer对象直接与一个int基本数据类型通过“==”比较,其结果与第一点相同。
④Integer对象的hash值为数值本身。
Integer对象的范围是-128~127:一个私有的静态内部类IntegerCache,而如果直接将一个基本数据类型的值赋值给Integer对象,则会发生自动装箱,其原理就是通过调用Integer类的public static Integer valueOf()将int类型的值包装到一个对象中。
一个字节占8bit,由于计算机只能识别二进制,即0和1。所以规定第一位是一个符号位,1表示负数,0表示正数,这里涉及到补码。
int的数值范围是-231~231-1(int占4个字节,即32bit)。

(6)包装类、装箱和拆箱

包装类(原始类型对应的类,即Boolean,Byte,Short,Integer,Long,Float,Double,Character)的自动拆、装箱分别是指如下的过程:
装箱:是指基本类型变为包装类型的过程,如Integer a=Integer.valueOf(100);或者int a=100; Integer b=new Integer(a);这里的Integer a=Integer.valueOf(100);new Integer(a);就是装箱,由基本数据类型构造出一个包装的对象。
拆箱:就是装箱的逆过程。如Integer a=Integer.valueOf(100); int b=a.intValue();这里的a.intValue()就是拆箱的过程,由一个包装类对象装换到相应的基本数据类型。
自动装箱、拆箱:指编译器帮助开发人员完成包装类的装箱和拆箱过程,也就是在将*.Java文件编译成*.class文件的过程中完成。
包装类的自动装箱和拆箱规则如下:

  1. 遇到赋值运算符“=”(包括传参数,参数传递属于隐式赋值)时,会将包装类拆箱为相应类型;
  2. 遇到算术运算符、位运算和位移运算符“+,++,–,-,*,/,%,&,|,……,~,<<,>>,>>>”时,对包装类进行拆箱;
  3. 遇到关系运算符“>,<,>=,<=”(不包括“==”和“!=”)是,对包装类进行拆箱
  4. 对关系运算符“==”和“!=”而言,遇到数字常量或者算术表达式时,才对包装进行拆箱。

其余情况下,不进行自动的装箱和拆箱。
包装类的equals方法不支持数据类型的自动转换。

(7)String、StringBuilder和StringBuffer

String,StringBuilder,StringBuffer 三者的区别主要是在两个方面,即运行速度和线程安全这两方面。
5. 运行速度,或者说执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer >String ,String最慢的原因:String为字符串常量,而StringBuild和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
6. 线程安全:在线程安全上,StringBuilder 是线程不安全的,而StringBuffer是线程安全的。
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
String:适用于少量的字符串操作的情况;
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况;
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况。

(8)重载和重写有什么区别

方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。

① 重载Overloading

  1. 方法重载是让类一统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载是一个类中多态性的一种表现。
  2. Java的方法重载,就是在类中可以创建多个方法。它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给他们的不同参数个数和参数类型来决定具体使用哪个方法,这就是多态性。
  3. 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不同。但是!不能以返回值类型作为重载函数的区分标准!

② 重写overriding

  1. 父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与父类有相同的名称和参数,我们说方法被重写(overridiing)。在Java中,子类可继承父类的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就是需要采用方法的重写。方法重写又称方法覆盖。
  2. 若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
  3. 子类函数的访问修饰权限不能少于父类的。

(9)抽象类和接口有什么区别

抽象类是用来捕捉子类通用的特性。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板,以JDK中的GenericServlet为例。
接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。

抽象类和接口的区别:

  1. 抽象类可以不包括静态方法,接口不能包含静态方法;
  2. 抽象类和接口都可以包含静态成员变量,抽象类中静态成员变量访问类型可以任意,但接口中定义的变量只能是public static final 类型,并且默认为public static final 类型;
  3. 抽象类中可以包括普通成员变量,接口中没有普通成员变量;
  4. 抽象类中的方法不能同时是静态的,抽象类中可以存在静态方法
参数抽象类接口
默认的方法实现它可以有默认的方法实现接口是完全抽象的,它根本不存在方法的实现
实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现子类使用关键字implements来实现接口。它需要提供接口中所有声明方法的实现
构造器抽象类可以有构造器接口不能有构造器
与正常Java类的区别除了你不能实例化抽象类之外,它和普通Java类没有任何区别接口是完全不同的类型
访问修饰符抽象方法可以有pubilc、protected和default这些修饰符接口方法默认修饰符是public。不可以使用其他修饰符
main方法抽象方法可以有main方法并且我们可以运行它接口没有mian方法,因此我们不能运行它
多继承抽象方法可以继承一个类和实现多个接口接口只可以继承一个或多个其它接口
速度它比接口速度要快接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法
添加新方法如果往抽象类中添加新的方法,可以给它提供默认的实现。因此不需要改变现在的代码如果往接口中添加方法,那么必须改变实现该接口的类

(10)反射的用途及实现

Java反射机制主要用于实现以下功能:

  1. 在运行时判断任意一个对象所属的类型。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时调用任意一个对象的方法,甚至可以调用private方法。

实现Java反射机制的API在Java.lang.reflect包下,具有以下几点:

  1. Class类:代表一个类。
  2. Filed类:代表类的成员变量。
  3. Method类:代表类的方法。
  4. Constructor类:代表类的构造方法。
  5. Array类:提供了动态数组及访问数组元素的静态方法。该类中的所有方法都是静态的。

在Java中实现反射最重要的一步,也是第一步就是获取Class对象,得到Class对象后可以通过该对象调用相应的方法来获取该类中的属性、方法以及调用该类中的方法。

Java中反射有如下几种实现方式

  1. 通过Class.forName()方法加载字符串,就可以得到改字符串做代表的Class对象。
    例如:Class<?>clazz=Class.forName(“Java.lang.String”)就可以得到String类的Class对象。值得注意的是,字符串必须是类的全名,即包名+类名。
  2. 通过类名调用class属性得到该类的Class对象。
    例如:Class<?>clazz=String.class也可以得到String类的Class对象。
  3. 调用示例的getClass()方法
    例如:
			Date date=new Date();
 			Class<?>clazz=date.getClass();
  1. 如果类是基本类型的包装类,则可以通过调用包装类的Type属性来获得该包装类的Class对象。
    例如:Class<?>clazz=Integer.TYPE;

持续搬砖中。。。