网上一份java基础面试题&整理的答案(或链接)①

网上看到一份号称 史上最全的Java工程师面试题汇总,全会的话月薪至少3W+ ,那就根据里面的内容,自己找找相关的答案

Java基础知识

简述:

1、==运算符是判断两个对象是不是同一个对象,即他们的地址是否相等

2、object类中equals与==是等效的

3、覆写equals更多的是追求两个对象在逻辑上的相等,你可以说是值相等,也可说是内容相等。(覆盖以后,覆盖equals时总要覆盖hashCode )

4、hashCode用于返回对象的hash值,主要用于查找的快捷性,因为hashCode也是在Object对象中就有的,所以所有Java对象都有hashCode,在HashTable和HashMap这一类的散列结构中,都是通过hashCode来查找在散列表中的位置的。

 

简述:

1、Integer是int的包装类,int则是java的一种基本数据类型 
2、Integer变量必须实例化后才能使用,而int变量不需要 
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 
4、Integer的默认值是null,int的默认值是0

简述:

1、为子类提供一个公共的类型;

2、封装子类中重复内容(成员变量和方法);

3、定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。

 

简述:

1、抽象类可以有构造方法,接口中不能有构造方法。

2、抽象类中可以有普通成员变量,接口中没有普通成员变量

3、抽象类中可以包含静态方法,接口中不能包含静态方法

4、 一个类可以实现多个接口,但只能继承一个抽象类。

5、接口可以被多重实现,抽象类只能被单一继承

6、如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法

 

简述:

我们是可以创建一个包含可变对象的不可变对象的,只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝,最常见的例子就是对象中包含一个日期对象的引用。

(补充说明:

不可变对象

  1. 可变对象:相对于不可变类,可变类创建实例后可以改变其成员变量值,开发中创建的大部分类都属于可变类。
  2. 不可变对象:是指一个对象的状态在对象被创建之后就不再变化。不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改。
  3. 那么对象是类实例化而来,怎么创建一个不可变类呢?

创建一个不可变类

一个不可变类,必须要满足以下条件:

(1)将类声明为final,所以它不能被继承;

(2)将所有的成员声明为私有的,这样就不允许直接访问这些成员;

(3)对变量不要提供setter方法;

(4)将所有可变的成员声明为final,这样只能对它们赋值一次;

(5)通过构造器初始化所有成员,进行深拷贝(deep copy);

(6)在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝;

(7)如果要修改类的状态,必须返回一个新的对象。

不可变类对于开发者来说有如下好处:

(1)易于设计,实现和使用

(2)使用过程中不容易导致出错

(3)更加的安全,可以随意地共用

(4)天然具备线程安全性,无需增加额外的同步操作

不可变对象的好处:

(1)不可变对象更容易构造,测试与使用;

(2)真正不可变对象都是线程安全的;

(3)不可变对象的使用没有副作用(没有保护性拷贝);

(4)对象变化的问题得到了避免;

(5)不可变对象的失败都是原子性的;

(6)不可变对象更容易缓存,且可以避免null引用;

(7)不可变对象可以避免时间上的耦合;

 

简述:

       【所谓多态,就是指程序中定义的引用变量所指向的具体类型通过该引用变量发出的方法调用编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用变量调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。】

       指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载父类的方法也不可以。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)

       对于面向对象来说,多态分为编译时多态运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数(如不带参的构造函数和带参的构造函数),通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性

       对于Java而言,它多态的实现机制遵循一个原则:当父类对象引用变量指向子类对象时,被引用对象的类型(也就是子类对象)而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在父类中定义过的,也就是说被子类覆盖的方法。

       基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。(如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。)

       在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。

      继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。

链接里有一个经典案例,最后的总结:

多态机制遵循的原则概括为:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

 

简述:

String     StringBuffer     StringBuilder
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间  StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量  可变类,速度更快
不可变     可变    可变
   线程安全     线程不安全
   多线程操作字符串    单线程操作字符串

 

简述:

1、

<? extends T>限定参数类型的上界:参数类型必须是T或T的子类型

<? super T> 限定参数类型的下界:参数类型必须是T或T的超类型
2、

上界<? extends T>不能往里存,只能往外取

下界<? super T>不影响往里存,但往外取只能放在Object对象里

<? extends T> 只能用于方法返回,告诉编译器此返参的类型的最小继承边界为T,T和T的父类都能接收,但是入参类型无法确定,只能接受null的传入
<? super T>只能用于限定方法入参,告诉编译器入参只能是T或其子类型,而返参只能用Object类接收既不能用于入参也不能用于返参

3

PECS原则(Producer Extends Consumer Super):

频繁往外读取内容的,适合用上界Extends。

经常往里插入的,适合用下界Super。

 

简述

1、进程资源分配的最小单位线程程序执行的最小单位(资源调度的最小单位)
2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

 

简述:
1.简单区别:
final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

2.中等区别:
虽然这个单词在Java中都存在,但是并没太多关联:
final:java中的关键字,修饰符。
A).如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。
B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变.
  1)被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
  2)被声明final的方法只能使用,不能重载。
finally:java的一种异常处理机制。
  finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的机率。
finalize:Java中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

3.详细区别参考链接

 

简述:

序列化:可以将对象转化成一个字节序列,便于存储。
反序列化:将序列化的字节序列还原
优点:可以实现对象的"持久性”, 所谓持久性就是指对象的生命周期不取决于程序。

序列化方式一: 实现Serializable接口(隐式序列化)
通过实现Serializable接口,这种是隐式序列化(不需要手动),这种是最简单的序列化方式,会自动序列化所有非static和 transient关键字修饰的成员变量。

序列化方式二:实现Externalizable接口。(显式序列化)
Externalizable接口继承自Serializable, 我们在实现该接口时,必须实现writeExternal()和readExternal()方法,而且只能通过手动进行序列化,并且两个方法是自动调用的,因此,这个序列化过程是可控的,可以自己选择哪些部分序列化

序列化方式三:实现Serializable接口+添加writeObject()和readObject()方法。(显+隐序列化)

如果想将方式一和方式二的优点都用到的话,可以采用方式三, 先实现Serializable接口,并且添加writeObject()和readObject()方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须有相应的格式。

1,方法必须要被private修饰                                ----->才能被调用
2,第一行调用默认的defaultRead/WriteObject(); ----->隐式序列化非static和transient
3,调用read/writeObject()将获得的值赋给相应的值  --->显式序列化

 

简述:

  • parseInt(String s)--内部调用parseInt(s,10)(默认为10进制)
  • 正常判断null,进制范围(radix在Character.MIN_RADIX与Character.MAX_RADIX之间是指在 2~~36之间),length等
  • 判断第一个字符是否是符号位
  • 循环遍历确定每个字符的十进制值
  • 通过*= 和-= 进行计算拼接
  • 判断是否为负值 返回结果。

(具体过程参考链接)

 

简述:

java中静态属性和静态方法可以被继承,但是没有被重写而是被隐藏.

原因:
1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制即可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。

2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。

3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。

静态属性和静态方法可以被继承
静态方法不可以被重写,不能实现多态

 

简述:

什么是内部类?
内部类是定义在另一个类中的类;即内部类对象引用了实例化该内部对象的外围类对象。

为什么需要内部类?
内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
内部类可以对同一个包中的其他类隐藏起来。
当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

内部类的分类
一、成员内部类

存在于某个类的内部的非静态类,与全局属性或者方法同级的内部类就是成员内部类。

成员内部类特点:
①成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括静态成员和私有成员)。
②成员内部类和外部类的属性和方法名同名时,外部类的属性和方法会隐藏;但可以通过外部类.this.成员变量的方式访问外部类的属性和方法。
③外部类必须通过成员内部类的对象来访问内部类的属性和方法。
④成员内部类对象会隐式的引用一个外部类对象。(可以解释第一点)
⑤成员内部类可以有public\private\protected以及默认访问权限。

二、静态内部类
在成员内部类的基础上加上一个static关键字就是静态内部类。

静态内部类的特点:
不需要依赖外部类。
不能使用外部类的非静态属性和方法。

项目中的使用:
Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象类,HashMap内部维护Entry数组用来存放元素,但是Entry对使用者是透明的。像这种和外部类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。

三、局部内部类
是定义在一个方法或者一个作用域里面的类,就是局部类。它与成员内部类的区别在于局部内部类的访问仅在于方法内或者作用域内。
(局部内部类是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,然而又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。)

局部内部类的特点:
不能有private、public、protected和static等修饰符,与局部变量类似。
只能在定义局部内部类的方法或者作用域中实例化;
局部内部类的对象不能使用该内部类所在方法或者作用域的非final局部变量(为什么?);

项目中的使用:
如果一个类只在某个方法中使用,则可以考虑使用局部类。

四、匿名内部类
不定义类的名字,在使用的地方直接定义对象。

匿名内部类的特点:
唯一一种没有构造器的类;匿名内部类在编译时,编译器会自动起名xxx$1.class;
匿名内部类不能存在任何静态的变量、方法等;
匿名内部类是局部内部类的特例;
大部分匿名内部类用于接口返回;
匿名内部类是没有访问修饰符的。
new 匿名内部类,这个类首先是要存在的。
当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。
匿名内部类没有明面上的构造方法,编译器会自动生成一个引用外部类的构造方法。

项目中的使用:
匿名内部类使用广泛,比如我们常用的绑定监听的时候。
使用匿名内部类,创建线程

(详细内容参考链接)

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章