Java面试经验个人总结(一)——Java基础

Java基础

一、常见问题

1. JDK和JRE的区别

(1)JDK是Java Development Kit,它是功能齐全的Java SDK。它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。它能够创建和编译程序

(2)JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java虚拟机(JVM),Java类库,java命令和其他的一些基础构件。但是,它不能用于创建新程序。它是用来运行Java程序

2. 基本数据类型

 

其中1Byte=8bit,1个字节等于8个比特。

3. 重载(overload)和重写(override)的区别

(1)重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
(2)重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。

4. Java面向对象编程三大特性

(1)封装:封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

(2)继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

a. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。

b. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

c. 子类可以用自己的方式实现父类的方法。

(3)多态:允许不同类的对象对同一消息做出响应。方法的重载、类的覆盖和接口的实现正体现了多态。

a. 多态机制:静态多态——overload;动态多态——implement,extends;

b. 声明的总是父类类型或接口类型,创建的是实际类型;

c. 多态的实现原理:

JVM 的方法调用指令有四个,分别是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前两个是静态绑定,后两个是动态绑定的。动态绑定,也是多态要解决的核心问题。

① A a = new B();

② A a 作为一个引用类型数据,存储在JVM栈的本地变量表中;

③ new B()作为实例对象数据存储在堆中;

④ 首先虚拟机通过reference类型(A的引用)查询java栈中的本地变量表,得到堆中的对象类型数据的地址,从而找到方法区中的对象类型数据(B的对象类型数据),然后查询方法表定位到实际类(B类)的方法运行。

5. 类加载顺序

(1)加载父类的静态字段或者静态语句块

(2)子类的静态字段或静态语句块

(3)父类普通变量以及语句块

(4)父类构造方法被加载

(5)子类变量或者语句块被加载

(6)子类构造方法被加载

6. String,StringBuffer和StringBuilder的区别。

(1)可变性:String类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以String对象是不可变的。而StringBuilder与StringBuffer 都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[] value但是没有用final关键字修饰,所以这两种对象都是可变的。

(2)线程安全性:String 中的对象是不可变的,也就可以理解为常量,线程安全;StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

(3)性能:String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象;

(4)使用:String适合操作少量数据;StringBuilder单线程操作字符串缓冲区操作大量数量;StringBuffer适合多线程操作字符串缓冲区下操作大量数据。

7. 成员变量和局部变量的区别

(1)成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。

(2)如果成员变量是使用static修饰的,那么这个成员变量是属于类的,如果没有使用static修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。

(3)成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。

(4)成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员变量也必须显示地赋值),而局部变量则不会自动赋值。

8. 在 Java 中定义一个不做事且没有参数的构造方法的作用

Java 程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。

9. 抽象类和接口的区别

(1)接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。

(2)接口中除了static、final变量,不能有其他变量,而抽象类中则不一定

(3)一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口

(4)接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰)。

(5)从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范

10. ==和equals

(1)==:它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)。

(2)equals():它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

a. 类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象;

b. 类覆盖了equals()方法。一般,我们都覆盖equals()方法来比较两个对象的内容是否相等;若它们的内容相等,则返回true,即使两个对象的地址不同。

11. hashCode()与equals()的相关规定

(1)如果两个对象相等,则hashcode一定也是相同的;

(2)两个对象相等,对两个对象分别调用equals方法都返回true,即A.equals(B)等价于B.equals(A);

(3)两个对象有相同的hashcode值,它们也不一定是相等的,即发生数据碰撞;

(4)hashCode()主要是根据内存地址来运算的,如果只重写了equals()而没有重写hashCode会导致两个明明相等的对象hash值不一样;

12. Java的值传递

Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

(1)一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。

(2)一个方法可以改变一个对象参数的状态。

(3)一个方法不能让对象参数引用一个新的对象。

13. final关键字

final关键字主要用在三个地方:变量、方法、类。

(1)对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是可以修改对象状态。

(2)当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。

(3)使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义,final变量是线程安全的;第二个原因是效率,JVM和Java应用都会缓存final变量。类中所有的private方法都隐式地指定为final。

(4)final原理

a. 先写入final变量,后调用该对象引用,原因是编译器会在final域的写之后,插入一个StoreStore屏障禁止指令重排

b. 先读对象的引用,后读final变量,编译器会在读final域操作的前面插入一个LoadLoad屏障禁止指令重排

14. 深拷贝和浅拷贝

(1)浅拷贝:对基本数据类型进行值传递;对引用数据类型进行引用传递;

(2)深拷贝:对基本数据类型进行值传递;对引用数据类型,创建一个新的对象,并复制其内容;

(3)clone()方法默认对状态是浅拷贝,但对对象本身是值传递;通过实现Cloneable接口,实现对象状态的深拷贝;

15. Java8新特性

(1)Lambda表达式,java语法糖的体现

(parameters) -> expression或(parameters) ->{statements; }。Lambda表达式是函数式编程的重要体现,函数式编程主要是为了解决面向对象设计中多线程的线程安全问题。函数式编程,数据不可变的,所以没有并发编程的问题,也更加高效,但是更加占用资源。

(2)方法引用

Object::Method

(3)函数式接口:Runnable,Callable等

(4)Default关键字,使得接口中可以实现默认方法,即允许被default修饰的接口方法拥有方法体。

(5)Stream对象:pipeline+内部迭代

a. Stream的获取:通过集合Collection;通过数组;直接通过值;

b. Stream的常用的管道操作:筛选filter;去重distinct;截取limit;跳过skip;映射map;合并流flatMap;匹配元素match;获取元素find;规约统计;遍历forEach;排序sorted;并行处理parallelStream

(6)Optional类

(7)Nashorn JavaScript

(8)日期时间API

(9)内嵌类

a. static class Base64.Decoder:该类实现一个解码器用于,使用 Base64 编码来解码字节数据。

b. static class Base64.Encoder:该类实现一个编码器,使用 Base64 编码来编码字节数据。

16. Java的序列化和反序列化

(1)Serializable接口

在序列化时,会检查一个类是否实现了Serializable接口,若是一个普通类(非数组,String,枚举)没有实现该接口,则否则会抛出NotSerializableException。

(2)serialVersionUID

serialVersionUID主要是用于反序列化的时候做匹配用的,只有serialVersionUID的值一致,最后反序列化的时候才认为它们是同一种类型,如果不定义,程序会默认生成一个1L。在反序列化的时候,就是依据这个值来跟当前虚拟机中的类中定义的数值比对,如果一致,才会认为是同一个版本,否则即使其他部分完全一样,也无法顺序反序列化。

(3)序列化的核心

在ObjectOutputStream类中的writeObject()方法中的writeObjectMethod.invoke(obj, new Object[]{ out })。包括容器类也是基于该方法实现序列化。

(4)transient关键字

a. transient关键字只能修饰变量,而不能修饰方法和类。本地变量是不能被transient关键字修饰的。

b. 被transient关键字修饰的变量不再能被序列化(static变量保持原值,且static的优先级大于transient)

c. 被transient关键字修饰的变量在反序列化时会被初始化

17. java的自动拆箱和自动装箱

(1)自动拆箱:obj.intValue()

(2)自动装箱:Integer.valueOf()

 

二、J2EE(Java 2 Platform, Enterprise Edition)

1. Servlet

Servlet主要负责接收用户请求HttpServletRequest,在doGet(),doPost()中做相应的处理,并将回应HttpServletResponse反馈给用户。Servlet可以设置初始化参数,供Servlet内部使用。一个Servlet类只会有一个实例(单例模式),在它初始化时调用init()方法,销毁时调用destroy()方法。Servlet需要在web.xml中配置,一个Servlet可以设置多个URL访问。Servlet是否线程安全是由它的实现来决定的,如果它内部的属性或方法会被多个线程改变,它就是线程不安全的,反之,就是线程安全的,Servlet本身是无状态的Servlet其本质是一个接口,定义了处理网路的规范,Servlet本身并不直接处理请求,也不直接与客户端通信(web容器和客户端通信)。

2. Servlet接口方法和生命周期

(1)接口方法:

a. void init(ServletConfig config) throws ServletException

b. void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException

c. void destory()

d. java.lang.String getServletInfo()

f. ServletConfig getServletConfig()

(2)生命周期

Web容器(例如:Tomcat)会根据web.xml配置文件加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。init方法和destroy方法只会执行一次,service方法客户端每次请求Servlet都会执行。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。以Spring为例来说,会进入FrameworkServlet---->DispatcherServlet分发---->各种拦截器—>业务处理----> 返回客户端。

3. 自动刷新

自动刷新不仅可以实现一段时间之后自动跳转到另一个页面,还可以实现一段时间之后自动刷新本页面。Servlet中通过HttpServletResponse对象设置Header属性实现自动刷新。

4. Servlet和线程安全

Servlet是否线程安全是由它的实现来决定的,如果它内部的属性或方法会被多个线程改变,它就是线程不安全的,反之,就是线程安全的,Servlet本身是无状态的。实例变量不正确的使用是造成Servlet线程不安全的主要原因。

线程安全的Servlet:

(1)实现 SingleThreadModel 接口;

(2)同步对共享数据的操作,对方法或者代码块加锁;

(3)避免使用实例变量,无状态对象设计也是解决线程安全问题的一种有效手段

5. JSP内置对象

(1)request:封装客户端的请求,其中包含来自GET或POST请求的参数;

(2)response:封装服务器对客户端的响应;

(3)pageContext:通过该对象可以获取其他对象;

(4)session:封装用户会话的对象;

(5)application:封装服务器运行环境的对象;

(6)out:输出服务器响应的输出流对象;

(7)config:Web应用的配置对象;

(8)page:JSP页面本身(相当于Java程序中的this);

(9)exception:封装页面抛出异常的对象。

6. JSP四种作用域

(1)page代表与一个页面相关的对象和属性。

(2)request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。

(3)session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。

(4)application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值