Java面试复习2 java的多态,异常处理及常用API

声明:本面试复习都基于一本未署名的Java面试宝典所述,根据自己的理解写出了这一专栏的复习博客

  1. Java中实现多态的机制是什么?
    简单来说就是定义一个父类的,或者接口类型的引用变量指向子类或者接口实现类的实例对象现在A是B的父类 Public A a = new B();这就是一种多态性的体现通过这种方式,父类定义的变量可以调用子类中的重写方法,而且在程序的运行时完成动态的绑定,而不再调用父类或者接口类型中的方法
  2. java中的异常处理
    首先要清楚异常按照处理时机分为两种,一种是编译时异常CheckedException(也叫强制性异常),一种是运行时异常RuntimeException(也叫非强制性异常)
    前者作为Java所特有的异常,在编译时期如果代码出现错误就将停止编译,编译器给出的两种处理方式为:try/catch:(当前方法知道怎么处理异常)
    抛出异常(当前方法不知道怎么处理异常)
    而后者是在实际开发中会经常碰到的错误,仅发生在代码运行时的异常,诸如空指针异常,除数为0的算术异常,数组下标越界等,一般情况下这些异常由系统捕获在控制台上显示给程序员,当然如果你想要获取这个异常的文本也可以手动捕获并输出或者增加操作,但这个并不会阻止当前程序的运行终止,该终止还是终止。
  3. 调用下图中方法,返回值是什么?

    正确答案:3 (感谢一楼纠错…)
    在这里我们需要认识一个点,当try/catch中的catch中遇到了return的时候,需要执行完整个try/catch结构再执行catch中的语句,正确的执行顺序为:try->catch(检查到其中有return)->finally->catch(再执行一遍)
    {这个代码的执行顺序是没错的,不过在finally中由于是return语句,而不是空操作或者赋值操作,那么当return执行后整个方法代码便结束了,也就回不到上面去了。}
public static int i;
    @Test
    public void testTest(){
        int a = returnTest();
        System.out.println(a);//此时返回的便是2
    }
    public int returnTest(){
        try{
            int a = 1/0;
            i = 1;
        }catch (Exception e){
            i = 2;
            return i;
        }
        finally {
            i = 3;
        }
        return i;
    }
  1. error和exception的区别?
  2. test包引入 java java test类_数组

  3. Java的异常处理机制
  4. test包引入 java java test类_子字符串_02

  5. 请写出5个最常见的RuntimeException
    NullpointException:空指针异常,当调用不存在或者未初始化对象的变量的时候会发生
    ClassNotFoundException:找不到指定类,当调用类的时候类名和路径加载错误时会发生,通常是程序员试图通过字符串来家在某个类的时候发生
    NumberFormatException:字符串转换为数字发生异常,当被转换的字符串中不仅仅是数字的时候会发生
    SQLException:SQL异常,当SQL语句错误时发生异常
    ClassCastException:数据类型转换异常
    IndexOutOfBoundsException:数组下标越界异常,当操作数组对象时操作数多于其长度会发生
  6. test包引入 java java test类_字符串_03


  7. test包引入 java java test类_test包引入 java_04


  8. test包引入 java java test类_test包引入 java_05

  9. throw和throws的区别
  1. throw用于方法体内,表示抛出异常,由方法体内的语句处理。
  2. throw是具体向外抛出异常的动作,所以他抛出的是一个异常实例,执行throw一定是抛出了某种异常,是处理
  3. throws使用在方发声明后面,表示如果抛出了异常,由该方法的调用者来进行处理。
  4. throws主要是声明这个方法会抛出什么类型的异常,让调用该方法的对象明白,需要按照相同的,或者更高级别的异常类型来处理。
  5. throws表明一种异常发生的可能性,是防范,并不一定会发生异常
  1. final,finally,finalize的区别
  1. final:用于声明属性方法和类,一旦声明,则所声明属性不可更改,所声明方法不能覆盖,所声明类不能被继承
  2. finally:异常处理try/catch语句中的一个部分,无论try/catch语句中发生什么,总是执行
  3. finalizeObject类中的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时其他资源的回收。可以理解为,某个对象的该方法一旦被调用,就意味着被调用的对象的生命周期的结束,但是值得注意的是,该方法不能被主动调用,而是被动的被调用(属于一种回调),因此如果我们主动调用该方法的时候,对象的生命周期并不会结束。
  1. JavaSE的常用API
  1. 如图

    答案:12 , 11 四舍五入不必多说
    这个时候我们来复习一下Math这个类的用法
Math.sqrt() : 计算平方根
Math.cbrt() : 计算立方根
Math.pow(a, b) : 计算a的b次方
Math.max( , ) : 计算最大值
Math.min( , ) : 计算最小值
Math.abs() : 取绝对值
abs 绝对值 
acos,asin,atan,cos,sin,tan 三角函数 
sqrt 平方根 
pow(double a,doble b) a的b次幂 
log 自然对数 exp e为底指数 
max(double a,double b) 
min(double a,double b) 
random() 返回0.0到1.0的随机数 
long round(double a) double型数据a转换为long型(四舍五入) 
toDegrees(double angrad) 弧度—>角度 
toRadians(double angdeg) 角度—>弧度
//Math.random,产生随机数
	public static void main(String[] args) {
		double r =   Math.random();
		// 0 --100 
		System.out.println(r);
		Random random = new Random();
		int i = random.nextInt();
		System.out.println(i);
		int i1 = random.nextInt(50);//0--50 不包括50
		System.out.println(i1);
	}
  1. switch能否作用在byte上,能否作用在long上,能否作用在String上?
    答案:byte,String(在JDK7之后可以)可以,long不行
    这个其实算是对switch语句的一个检查,和对java版本的一个知悉程度。
    在这里我们借助此题完成复习,需要知道的是
  1. switch(表达式)中表达式的值必须是下述几种类型之一:byte,short, char,int,枚举类型enum(jdk 5.0),String (jdk 7.0)
  2. case子句中的值必须是常量,不能是变量名或不确定的表达式值;
    同一个switch语句,所有case子句中的常量值互不相同;
  3. break语句用来在执行完一个case分支后使程序跳出switch语句块;如 果没有break,程序会顺序执行到switch结尾
  4. default子句是可任选的。同时,位置也是灵活的。当没有匹配的case时, 执行default
  1. 数组有没有length()方法,String呢?
    答案:数组是没有的,但是每个数组都具有一个lenth属性String则是有该方法的。
  2. String,StringBuilder,StringBuffer的区别。
    Java提供了两种类型的字符串:一类是String,另一类则是StringBuilder/StringBuffer,都可以储存和操作字符串。
    区别:
  1. String是只读的字符串,也就意味着,它引用的字符串内容不能发生改变的,具体我们可以思考一下字符串常量池的概念就可以很好地理解了。
  2. StringBuilder/StringBuffer表示的字符串对象是可以修改的。
  3. 如图,前者效率在理论上比后者高
  1. 什么情况下,使用"+"运算符进行字符串连接比调用StringBuilder/StringBuffer对象的append方法连接字符串的性能更好?
    这个例题直接粘作者的解析,因为我也不是很懂,刚刚理解的样子,比较多,如下。
    总结就是,在一些简单的情况下,"+"和StringBuilder/StringBuffer的底层效果其实是相同的,+的底层也是转换成了StringBuilder/StringBuffer,但是复杂情况下,比如 +在涉及到循环时会有多次在循环中创建StringBuilder/StringBuffer的对象 ,而这种做法在程序中显然是不合适的,占用了太多的内存空间,并且产生的垃圾也会变多,不可取,因此后者的性能显然是更好的,即便是前者会令开发的程序语言显得简洁一些。但是后者依然在整个过程中更为合适。当然值得注意的是StringBuilder/StringBuffer两者之间也是有区别的,前者在JDK1.4之前并没有在API中提供使用,因此之前的版本是不存在他的,在J2SE5及以上版本之后,才有提供,由于后者是线程安全的,前者是线程不安全的,除去安全性的考虑,单考虑效率方面,前者的效率会更高。

    拓展思考:String,StringBuilder/StringBuffer类的了解
    String:不可变字符串 类中的一些方法。
int length():返回字符串的长度: return value.length 
char charAt(int index): 返回某索引处的字符return value[index] 
boolean isEmpty():判断是否是空字符串:return value.length == 0 
String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写 
String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写 
String trim():返回字符串的副本,忽略前导空白和尾部空白 
boolean equals(Object obj):比较字符串的内容是否相同 
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大 小写 
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+” 
int compareTo(String anotherString):比较两个字符串的大小 
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从 beginIndex开始截取到最后的一个子字符串。 
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字 符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束 
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始 
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的 子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列 时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引 
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出 现处的索引,从指定的索引开始 
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引 
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后 一次出现处的索引,从指定的索引开始反向搜索 注:indexOf和lastIndexOf方法如果未找到都是返回-1
String replace(char oldChar, char newChar):返回一个新的字符串,它是 通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 
String replace(CharSequence target, CharSequence replacement):使 用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 
String replaceAll(String regex, String replacement) : 使 用 给 定 的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 
String replaceFirst(String regex, String replacement) : 使 用 给 定 的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。 
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此 字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

String与基本数据类型之间的转换:

字符串 --> 基本数据类型、包装类 
Integer包装类的public static int parseInt(String s):可以将由“数字”字 符组成的字符串转换为整型。 
类似地,使用java.lang包中的Byte、Short、Long、Float、Double类调相应 的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。 
基本数据类型、包装类 --->字符串 
调用String类的public String valueOf(int n)可将int型转换为字符串 
相应的valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可由参数的相应类型到字符串的转换

字符数组 -->字符串
String 类的构造器:String(char[]) 和 String(char[],int offset,int length) 分别用字符数组中的全部字符和部分字符创建字符串对象。
字符串 -->字符数组 
public char[] toCharArray():将字符串中的全部字符存放在一个字符数组 中的方法。 
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将指定索引范围内的字符串存放到数组中的方法。
字节数组 -->字符串 
String(byte[]):通过使用平台的默认字符集解码指定的 byte 数组,构 造一个新的 String。
String(byte[],int offset,int length) :用指定的字节数组的一部分, 即从数组起始位置offset开始取length个字节构造一个字符串对象。
字符串 -->字节数组 
public byte[] getBytes() :使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 
public byte[] getBytes(String charsetName) :使用指定的字符集将 此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

StringBuilder/StringBuffer:可变字符串 区别在于前者线程不安全,效率高,后者线程安全,效率低。下列是一些方法,这两个都适用

StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接 
StringBuffer delete(int start,int end):删除指定位置的内容 
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str 
StringBuffer insert(int offset, xxx):在指定位置插入xxx 
StringBuffer reverse() :把当前字符序列逆转

public int indexOf(String str) 
public String substring(int start,int end) 
public int length() 
public char charAt(int n ) 
public void setCharAt(int n ,char ch)
  1. 下列程序的输出为:
    这是对于整个String和StringBuffer/StringBuilder,字符串常量池和intern的理解,
    首先是String.intern()
    String.intern()是一个Native方法,底层调用C++的 StringTable::intern 方法,源码注释:当调用 intern 方法时,如果常量池中已经该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,并返回字符串的引用。
    在知道了这个前提下。
    我们需要知道,s1创建了一个"Programing"常量存放在字符串常量池中,s2在堆内存中new了一个地址值,这个地址值中并没有我们所要的"Programing"常量,而是又指向了字符串常量池中的"Programing"常量,s3创建了一个"Progra"常量存放在字符串常量池中,s4创建了一个"ming"常量存放在字符串常量池中,s5进行拼接后,首先在字符串常量池中搜寻是否有对应的拼接后的"Programing"常量值的存在,答案是有的,那就不用创建了,让s5直接指向了"Programing"的位置,s6则是在变量的基础上进行的,这时候就需要翻底层操作了,也就是他需要
  1. 运行期JVM首先会在堆中创建一个StringBuilder类,
  2. 同时用s3指向的拘留字符串对象(intern字符串对象)完成初始化,
  3. 然后调用append方法完成对s4所指向的拘留字符串的合并,
  4. 接着调用StringBuilder的toString()方法在堆中创建一个String对象,
  5. 最后将刚生成的String对象的堆地址存放在局部变量s6中

而他指向的肯定是在堆内存中的地址,而不再是指向常量池中的常量了。所以我们可以由此得出下列结果了,但是当他重新调用intern方法时,就又指向了常量池中的常量了,对于intern来说,new过的对象,JDK6的执行结果为:false

JDK7和JDK8的执行结果为:true,原因是6中字符串常量池并不再堆内存中,而7和8在堆内存中了


test包引入 java java test类_字符串_06


test包引入 java java test类_字符串_07

因此就可以得出上述结论了。