【JAVA面试题整理】JAVA基础(一)

面向对象

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

1、继承

继承就是从已有的类中得到已有信息创建新类的过程,这个过程中,已有的类就是父类(超类、基类),新创建的类就是子类(派生类);继承让新的类有了一定的延续性

2、封装

封装通常意义理解为把数据和操作数据的方法分开实现,将所有的数据项进行封装,最终表现为对数据进行隐藏,对方法进行暴露,只向外部提供最简单的实现接口

3、多态

多态指不同的子类对象对同意操作做出不同的响应,比如A系统访问B系统时,B系统提供多种服务方式,但是一切对于A来说都是透明的;

多态分为运行时多态和编译时多态,方法重载(overload)是编译时多态(也称前绑定),方法重写(override)是运行时多态(也称后绑定);

运行时多态是面向对象实现多态最精髓的东西,要做一下两件事:

  1. 重写父类方法;

  2.  对象造型(用父类引用子类对象,这样会根据子类实现进而完成不同的实现方法,如Animal a= new Dog();Animal b = new Cat(););

4、抽象

抽象指对对象的属性和行为进行抽象化,在高度概括的基础上,归纳出它的数据属性和抽象行为,而不关注具体实现。如接口和抽象类,本质上都只是整理出类的骨架,具体的实现方法放在子类中进行实现

二、访问权限修饰符public、private、protected以及不写(默认)的区别

修饰符

当前类

同包

子类

其他包

public

protected

×

default

×

×

private

×

×

×

三、为什么要clone?

当一个对象A已经赋了一些值,这时再重新new一个对象,再赋上同样的值成本太高,所以才要clone

四、new一个对象和clone一个对象的区别?

new一个对象要先查看new对象后面的对象类型,根据对象类型进行内存的分配,然后再调用构造函数,填充各个域,这一步叫对象的初始化,之后再把对象的引用(地址)发布到外部,在外部就可以访问、操作这个对象。

clone一个对象第一步和new对象一样,都是根据对象的类型分配内存,然后根据原对象对clone出来的对象的各个域进行填充,最后再把对象的引用发布到外部。

五、复制对象和clone的区别

看一下打印的结果:

对象的地址是一样的,说明这两个对象是一样的。

这种方式叫做对象的复制,p和p1都只是对象的引用而已,对象只有一个。

下面的代码实现了真正的clone:

看一下他的引用

从打印结果上看,两者的引用地址是不一样的,说明这是两个不同的对象。

这是clone时,p和p1在内存中的场景,两者指向的是不同的对象,只是对象的类型和值相同。

六、深拷贝和浅拷贝

上述代码实例中,对Person对象已经进行了拷贝。由于age是基本类型int型,这个直接把具体的值拷贝过来没有什么争议,但是name这个String类型的字段,这个String对象本身就是引用,指向一个真正的String对象,那么拷贝它有两种方式:

  1. 直接将原对象中引用值拷贝给新对象的name字段

  2. 根据原Person对象中的name指向,创建一个新的相同的String对象,将这个新的String对象的引用赋给新拷贝的Person对象的name字段

第一种方式叫浅拷贝,第二种叫深拷贝。

下面通过代码对clone方法属于浅拷贝还是深拷贝进行验证

打印结果为

所以p.clone()的方法是浅拷贝的。

那么如何进行深拷贝?这就需要实现Cloneable接口,并且在clone()方法内部,把对应要深拷贝的对象也clone一份:

打印结果为:

body和body1不是同一个对象,两个head也不指向同一个地址,说明这种clone的方式是深拷贝。

JAVA语法

一、Java有没有goto语句?

goto是java语言中的保留字,但是并没有被使用。

二、&和&&的区别

&有两种运算方式:

  1. 按位与;

  2. 逻辑与;

&&是短路与运算,当&&前的表达式为false的时候,&&后的标的是就不再参与运算了。||运算符也同理。

三、如何跳出当前多重嵌套循环?

使用break和continue;

continue表示跳出本次循环;

break表示退出当前循环;

四、两个对象值相同(x.equals(y)==true),但是却有不同的hashcode,这句话对不对?

不对,如果x.equals(y)==true,那么x、y的hashcode应该相同。

Java对于hashcode和equals是这样规定的:

  1. 如果两个对象相同(equals),那么他们的hashcode一定相同;

  2. 如果两个对象的hashcode相同,那么他们的值(equals)不一定相同;

如果违反了上述的规则,就会导致Set集合中出现重复的对象,同时增加新元素的性能大大下降,因为hash碰撞的几率急剧上升

五、那么为什么重写equals的时候要重写hashcode呢? 

因为从源码上看,hashCode是native方法,读取的是内存地址。

public native int hashCode();

而equals方法以String的源码为例

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

它是使用==的方式,判断两个对象是否相同,如果不是一个对象,然后再判断String里面的值是否相同。

大家都知道,hashCode的查询复杂度是O(1),而equals的复杂度要看equals方法的具体实现,但普遍来讲,equals的复杂度要比hashCode查询慢。

所以,在同时重写了equals和hashCode方法之后,我们就可以先对hashcode进行比较,当hashcode相等的时候再比较equals,这样能对性能有较大的提升。

但是也要注意的是,hashcode方法主要使用场景是hashSet和hashMap,这两个实现类需要对hashcode进行判断,将值放入到对应的位置中。此时如果不重写hashcode方法,就容易产生hashSet中存在重复对象的可能。其余场景其实只重写equals问题也不大。

但是为了确保程序的正确和稳定性,我们建议在重写equals方法的同时,重写hashcode方法。

六、是否可以继承String?

String本身是fianl修饰的类,不可以继承

public final class String

七、java中对象作为参数,是值传递还是引用传递?

答:值传递

java中只有值传递:

  • 基本类型:值传递

  • 引用类型:地址值传递

当对象作为参数进行传递的时候,传递过去的都是副本:

  • 基本类型传递的是值得拷贝,不管副本如何变化,原有的值都不会有变化;

  • 引用类型传递的是引用的地址值,有可能被影响;

  • String是特殊的引用类型,因为它是final修饰的,具有不可变性,所以不管怎么传递,返回的String类型对象还是最初传递过去的值;

八、重载和重写的区别?

重载和重写都是多态的具体实现方式,只是重载时编译时多态,重写是运行时多态;重载需要有不同的参数列表,而重写要发生在子类和父类之间,两者有相同的方法名称,相同的参数列表,相同的返回类型。

重载的规则:

  1. 方法名一致,参数的顺序、个数、类型不同;

  2. 重载和返回值无关,存在于同类中;

  3. 可以抛出不同的异常,有不同的修饰符;

重写的规则:

  1. 参数列表必须完全与重写的方法一致,返回类型也必须一致;

  2. 构造方法、final方法、static方法不能被重写,但是这些可以重写声明(在子类中“重写”该方法,但是不加@override注解);

  3. 访问权限不能比父类的访问权限更低;

九、为什么函数不能根据返回类型来重载?

该题来自华为面试题。

参考如下代码

当我要调用max(1,2)时,对于上述两个方法是无法确认我要具体调用哪个方法的,这样违反了对方法调用的唯一性,造成的结果就是程序产生意想不到的效果,甚至崩溃。

并且函数的返回值,只是作为函数运行之后的一种状态描述,目的是产出一个和其他方法之间进行通信的标识,并不能将这个标识作为是否能重载的依据。

十、一个char类型的变量能否存储一个汉字?

可以。

我们先补充一下相关的知识:

1字节=8btye

1英文字母=1字节,1汉字=2字节(ASCLL、Unicode编码)

在UTF-8编码下,1汉字=3字节

而1个char类型变量占2个字节,作为Unicode编码的汉字来讲,是可以存储的,也就是我们的答案,但是某些不包含在Unicode编码中,使用UTF-8编码的汉字,是不能用char类型变量表示的。但是补充来讲,在JVM内部是使用Unicode作为默认编码,Unicode使用2个字节表示一个汉字,那么char类型就可以用存储一个汉字

十一、抽象类(Abstract Class)和接口(Inteface)有什么异同?

不同:

抽象类:

  1. 抽象类中可以定义构造器;

  2. 可以有抽象方法和具体方法;

  3. 抽象类中成员可以使public、private、protected和默认方法;

  4. 抽象类可以定义成员变量;

  5. 抽象类必须用abstract声明,但是抽象类中未必要有抽象方法;

  6. 抽象类中可以包含静态方法;

  7. 一个类只能继承一个抽象类;

接口:

  1. 接口中不能定义构造器;

  2. 接口的方法必须全部都是抽象方法;

  3. 接口的成员必须全部都是public的;

  4. 接口中定义的成员变量实际上都是常量;

  5. 接口中不能有静态方法;

  6. 一个类可以实现多个接口;

相同:

  1. 不能够被实例化;

  2. 可以将抽象类和接口类型作为引用类型;

  3. 一个类如果继承了抽象类或者实现了接口,要对其中全部的抽象方法进行实现,否则该类仍需要被声明为抽象类;

十二、静态变量和实例变量的区别

静态变量:使用static进行修饰,属于类变量,存放在栈中,是可以被共享的变量;

实例变量:必须依存某个已经初始化的对象才能进行访问;

十三、==和equals的区别

==对于基本类型来讲,比较的是基本类型的值,对于引用类型来讲,比较的是引用类型的地址的值

equals不能用来比较基本类型,对于引用类型,比较的是两个引用类型的内容是否相同

JAVA中的多态

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

多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译的时候并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪一个类的实例对象,该引用变量发出的方法调用到底是哪一个类中的方法,必须在程序运行期间才能决定!

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

JAVA的异常处理

一、Java中异常分为哪几类?

分为编译时异常(强制性异常),CheckedException和运行时异常(非强制性异常)RuntimeException。对于编译时异常要进行显式处理,否则会导致编译不通过。

处理异常分为两种情况:

  1. 对于清楚该如何处理的异常使用try..catch进行捕获;

  2. 对于不知道该如何处理的异常使用throws在方法声明的时候进行抛出;

二、try、catch、finally执行的优先级

try包裹的方法中一旦出现异常,就会走到catch的异常捕获方法中,但是如果有finally方法,在异常方法返回之前,一定要先执行finally里面的方法。举例如下

由于第3行除数为零,直接跳转到catch方法中,在catch的return命令之前要先执行finally里面的方法,所以最终返回结果是3。如果finally里面只是关闭流的操作,不进行返回的话,最终的返回结果就应该是2。

三、Error和Exception的区别

Error和Exception的父类都是Throwable类,他们的区别如下:

Error一般指虚拟机相关的异常,如系统崩溃、虚拟机错误、内存不足、方法调用栈溢出等。这类错误会直接导致程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议程序终止。

Exception分为运行时异常(RuntimeException)和编译时异常(CheckedException),运行时异常如ArithmeticException、IllegalArgumentException等,编译能通过,但是一旦遇到这种异常程序就终止了 ,程序不会处理运行时异常;而编译时异常可以使用try catch或者throws进行异常处理。

四、写出最常见的5个编译时异常

  1. java.lang.NullPointException 空指针异常;出现原因:调用了未初始化的对象或是不存在的对象;

  1. java.lang.ClassNotFoundException 指定类找不到;出现原因:类名和路径加载错误;

  2. java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符串中出现非数字类型字符;

  3. java.lang.IndexOutOfBoundsException 数组下标越界异常;出现原因:访问数组对象下标超过本身数组长度;

  4. java.lang.IllegalArgumentException 方法参数传递错误;

  5. java.lang.ClassCastException 类转换异常;出现原因:类之间不能进行强制转换;

  6. java.lang.NoClassDefFoundException 未找到定义类;

  7. SQLException SQL编写错误;

  8. java.lang.InstantiationException 实例化异常;

  9. java.lang.NoSuchMethodException 未找到方法异常;

五、throw和throws的区别

throw:

  1. throw用在方法体内,表示抛出异常,由方法体内的语句处理;

  2. throw抛出的一般是一个异常实例;

throws:

  1. 用在方法声明后面,表示如果存在异常,由方法的调用者进行异常处理;

  2. throws抛出某种异常类型,让调用者清楚使用何种方法进行异常处理;

  3. throws表示一种可能性,并不代表异常会发生;

发布了83 篇原创文章 · 获赞 58 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qixinbruce/article/details/104670084
今日推荐