Java面试题总结(未完待续)

Java面试题总结

封装、继承、多态

1、Java中实现多态的机制是什么?

java特性:继承、封装、多态。
Java中实现多态的条件:继承、重写、向上转型
继承: 子承父业。
重写: 子类对父类中的某些方法进行重新定义,如重写equals方法。
向上转型: 将子类的引用赋给父类对象,这样该引用才具备同时调用父类方法和子类方法的资格。

实现形式: 继承(extends)和实现接口(implements)

静态多态和动态多态的区别?
答:多态分为编译时的多态和运行时的多态,前者是静态的,后者是动态的。
静态多态如方法的重载,在程序编译时期就已经确定了调用哪个函数;
而动态多态是在运行时才能动态地确定操作指针所指的对象,主要通过重写来实现。
java的多态机制遵循一个原则:当父类对象引用变量引用子类对象时,被引用的对象类型决定了调用谁的成员方法,而不是引用变量的类型去决定,但是这个被调用的方法必须是在超类中定义过的,也就是被子类覆盖的方法。

2、抽象类和接口的区别,如何选择?

抽象类
抽象类抽象出了一个实体的通用特性,通用性高,更抽象。有一个或多个抽象方法的类本身应该被声明是抽象的,除了抽象方法外,抽象类还可以包含具体的数据和具体的方法。抽象方法充当着站位的角色,他们的具体实现应该放在子类中,抽象类不能被实例化,如一个抽象类Person person = new Person();是错误的,但可以创建抽象类的对象变量,如Person person = new Student();其中student是person的实现类,Student类是非抽象类才可以。
接口
Java对接口给出的定义:接口不是类(不能使用new运算符实例化一个接口,但可以声明接口变量,接口变量必须引用实现了接口的类对象),而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。接口中所有的方法自动属于public,所以定义接口时不必提供关键字public!接口可以被扩展(继承其他接口)。接口中不能包含实例域或静态方法,但接口可以包含常量。接口是抽象方法的集合。如果一个类实现了某个接口,这个类就要实现接口的所有方法。

为什么要引入接口概念,而不直接使用抽象类呢?
Java不予许多重继承,但允许有多种实现。引入接口就避免了多重继承,也就避免了多重继承带来的复杂性和低效性。

抽象类与接口的对比

抽象类 接口
实现 关键字extends。如果子类不是抽象类,它需要提供抽象类中所有的方法的实现 关键字implements。子类需要实现接口的所有方法
构造器 抽象类可以有构造器 接口没有构造器
访问修饰符 public 、private、default都可以 方法默认标记为public,域标记为public static final
多继承 抽象类可以继承一个类和实现多个接口 接口可以继承一个类或多个类
方法 抽象类可以在类本身中添加具体实现代码 接口方法内不能加入具体的代码实现

** 如何选择?**
抽象类和接口抽象出的东西的概念不一样,可以这样说:抽象类是对某一类事物的本质进行抽象,接口是对某一类事物的功能进行抽象。

抽象类表示这个对象是什么,接口表示这个对象能做什么。比如Person类抽象出这是人类,是个抽象类,人可以吃饭睡觉,其他动物也可以,于是就把吃饭睡觉抽象成接口,不同的动物去实现它。

所以当关注事物的本质的时候用抽象类,当关注一个操作的时候用接口。

3、重写和重载的区别?

重写(Override)
当某一子类继承了某一父类,这个子类继承了父类的所有方法,于是也可以对父类的方法进行重新编写,如重写toString()方法(你继承了你父亲的自然卷,你也可以将头发重新拉直,这是你的权利),使其继承下来的方法得到改变。需要注意的是子类重写的方法名,参数列表,返回类型必须跟父类相同,子类的访问权限修饰符不能小于父类的。
重载(Overload)
在同一个类中,同一个方法可以通过参数列表(参数类型、参数数量、参数顺序)不同则为重载,重载不关心返回类型是否相同,可同可不同,但不能由返回类型不同来判断为重载。

重载、重写与多态
重载和重写都是实现多态的方式,但区别在于重载是编译时多态,静态多态,即在编译时期就已经知道了该使用哪一个实现方法。重写是运行时多态,是动态多态,即在程序运行时才能知道引用对象具体使用的哪一个实现方法。

4、是否可以继承String类?

String类的定义为:public final calss String extends Object ,所以,String类是不可以被继承的,因为它被修饰为了final类。

final关键字
可以用来修饰变量、方法和类,用关键词final修饰的域成为最终域。用关键词final修饰的变量一旦赋值,就不能改变,也称为修饰的标识为常量。如果一个类的域被关键字final所修饰,它的取值在程序的整个执行过程中将不会改变。

假如说整个类都是final,就表明自己不希望从这个类继承,或者不答应其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类处理。

5、构造器能否被重写?

Constructor(构造器)不能被重写,但是可以被重载,如有参构造方法和无参构造方法。

6、public、private、protected的区别?

修饰符 当前类 同包 子类 其他包
public
protected ×
default × ×
private × × ×

集合

1、列举几个Java中Collection类库中的常用类

List、set、queue接口均继承与Collection接口,List接口又有ArrayList、LinkedList等实现类。Set接口又有HashSet、TreeSet等实现类。注意的是Map接口并不是Collection的子接口,而是同级的并列接口,Map有HashMap、HashTable等实现类。

2、List、Set、Map是否都继承自Collection?他们的存储特点分别是什么?

List和Set继承自Collection接口,但Map是“map”类的总接口,与Collection同级。
List特点:元素可重复存放,可以有序存放
Set特点:元素不可重复存放,无序存放
但TreeSet可以保持集合的有序性,支持自然排序和自定义排序
Set集合如何去重的
Map特点:以键值对key-value形式存放,如果键重复,那么后加入的值会覆盖之前的值。

3、ArrayList、LinkedList、Vector之间有何区别和联系?

ArrayList 是基于动态数组实现的
LinkedList 是基于链表实现的(准确来说应该是双向链表)
Vector 跟ArrayList实现方式差不多,但它在他的get、remove、set、size等操作方法上加了synchronized进行同步处理,理论上是线程安全的。但有些时候依然会出现同步问题(如抛出数组越界问题)synchronized的意思就是当一个线程访问某个方法时让其他线程不可以访问此方法,那为什么vector会出现同步问题呢?因为当同一个线程访问两个同步方法如get和remove方法时,删除了又去访问就会数组越界。
它们都继承自List接口。

增删改查四个方面比较ArrayList和LinkedList
增(插入新数据)(LinkedList优于ArrayList)
ArrayList是基于动态数组实现,当调用add方法增加发现容量不够时,会进行自动扩容。会创建一个更大的数组,然后将原数组中的数据拷贝至新数组中。
数组的增加操作可能会引起扩容操作,此时需要将原数组copy到新数组,再进行插入数据。若在数组中间插入数据,则需要将插入位置以后的数据分别向后移动一个单位。时间复杂度都挺高的(O(n)),若在数组末尾插入数据但不需要扩容时,插入效率高,时间快。
LinkedList是基于双向链表,在链表任何一个位置插入元素效率都很高,只需要将插入位置的相应头指针和尾指针变换一下指向就行了。时间复杂度为O(1)。

删除(LinkedList优于ArrayList)
ArrayList删除元素,需要将被删除元素后面的元素都向前移动一个单位,O(n)。如果删除元素后数组元素个数大大小于容量,还要进行缩容操作。
LinkedList 变换指针指向,O(1)。

查询元素(通常ArrayList优于LinkedList)
如果明确知道该元素位置&#

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,泛型是一种强类型机制,它可以让你在编译时检查类型错误,从而提高代码的安全性和可读性。在使用泛型时,我们经常会遇到父类和子类的泛型转换问题。 首先,我们需要明确一点:子类泛型不能转换成父类泛型。这是因为Java中的泛型是不协变的。例如,如果有一个类A和它的子类B,那么List<A>和List<B>之间是不存在继承关系的。 下面我们来看一个例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 } } ``` 在这个例子中,我们定义了Animal类和它的子类Dog。然后我们定义了两个List,分别是List<Animal>和List<Dog>。如果将List<Dog>赋值给List<Animal>,会出现编译错误。这是因为List<Animal>和List<Dog>之间不存在继承关系。 那么,如果我们想要让子类泛型转换成父类泛型,应该怎么办呢?这时我们可以使用通配符来解决问题。通配符可以表示任意类型,包括父类和子类。例如,我们可以将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 下面我们来看一个使用通配符的例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 List<? extends Animal> list3 = new ArrayList<>(); list3 = list2; // 正确 } } ``` 在这个例子中,我们定义了List<? extends Animal>来表示任意继承自Animal的类型。然后我们将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 总结一下,Java中的泛型是不协变的,子类泛型不能转换成父类泛型。如果需要实现子类泛型转换成父类泛型,可以使用通配符来解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值