一.基本概念和语法
1.switch语句中,表达式的数据类型,可以是哪些?
switch语句中,表达式的数据类型,可以是byte,short,char,int,enum(枚举),JDK7后可以接收字符串
2.==和equals 的对比
(1)==既可以判断基本类型,又可以判断引用类型 ①如果判断的是基本类型,则判断的是值是否相等 ②如果判断的是引用类型,则判断的是地址是否相等,即判断是不是同一个对象 (2)equals是Object类中的方法,只能判断引用类型。默认判断的是地址是否相等,子类一般都会去重写该方法。
重写object类的equals方法的注意事项
(1)自反性:对任意引用值X,x.equals(x)的返回值一定为true. (2)传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true (3)非空性:任何非空的引用值X,x.equals(null)的返回值一定为false (4)一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变 (5)对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
面向对象和面向过程的区别
(1)二者都是一种思想 (2)面向过程:强调的是功能行为,以函数为最小单位,用函数把这些步骤一步一步地实现。性能较高,单片机、嵌入式开发等一般采用面向过程开发 (2)面向对象:将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。面向对象具有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要低。
八种基本数据类型的大小,以及他们的封装类?以及Java有哪些数据类型?
除了上述所说的八种基本类型外,Java 还提供了一种引用数据类型的支持,比如我们常说的类(class)、接口(interface)和数组。
类型有哪些转换方式?
对于基本类型而言,主要分为自动(隐式)类型转换和强制(显式)类型转换两种方式。 (1)自动类型转换:是一种小类型到大类型的转换,不需要强制转换符。自动类型转换常出现在以下常景: ①小的类型自动转化为大的类型; ②整数类型可以自动转化为浮点类型,可能会产生舍入误差; ③字符可以自动提升为整数。 (2)强制类型转换:需要在强制类型转换的变量前面加上括号,然后在括号里面标注要转换的类型。强制类型转换中一些需要注意的事项: ①强制类型转换可能导致溢出或损失精度;; ②浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入; ③不能对布尔值进行转换; ④不能把对象类型转换为不相干的类型。
Integer a = 100;Integer b = 100; System.out.println(a==b); 结果为什么是true?
(1)在Integer内部用到了享元模式的设计,通过共享已经存在的对象来大幅度减少要创建的对象数量,从而减少内存的占用,提升性能;(2)Integer内部维护了一个IntegerCache,它缓存了-128~127之间数值对应的Integer类型,一旦程序调用valueOf方法如果数字在-128~127之间,就直接从cache里面去获取Integer对象,否则就会创建一个新的对象;
(3)两个Integer对象,因为数值都是100,并且默认通过装箱机制调用了valueOf方法从IntegerCache中拿到了两个完全相同的Integer实例,所以a==b的运行结果是true;
instanceof关键字的作用
(1)instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法
为:boolean result = 对象 instanceof 类/接口
(2)当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
(3)注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
int i = 0;
System.out.println(i instanceof Integer);//编译不通过 必须是引用类型,不能是基本类型
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果obj为null,那么将返回false。
System.out.println(null instanceof Object);
4.java 8接口新增了哪些特性?
增加了default方法和static方法,这两种方法可以有方法体
// default方法
public interface LiveAble {
public default void fly(){
System.out.println("天上飞");
} }
// static方法
public interface LiveAble {
public static void run(){
System.out.println("跑起来~~~");
}}
Java的三大特性
(1)封装 Java中的封装是指一个类把自己内部的实现细节进行隐藏,只暴露对外的接口(setter和getter方法) (2)继承 它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”,被继承的类称为“父类”。 (3)多态 ①多态是指多种状态,就是说当一个操作在不同的对象时,会产生不同的结果。 ②在Java中,实现多态的方式有两种,一种是编译时的多态,另外一种是运行时多态,编译时的多态是通过方法的重载实现的,而运行时多态是通过方法的重写实现的。 a.方法的重载是指在同一个类中,有多个方法名相同的方法,但是这些方法有着不同的参数列表,在编译期我们就可以确定到底调用哪个方法。 b.方法的重写,子类重写父类中的方法(包括接口的实现),父类的引用不仅可以指向父类的对象,而且还可以指向子类的对象。当父类的引用指向子类的引用时,只有在运行时才能确定调用哪个方法。
5.重写与重载的区别
(1)重写: ①发生在父类与子类之间 ②方法名,参数列表,返回类型(除了子类中方法的返回类型是父类中返回类型的子类)必须相同 ③访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private) ④重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查性异常 (2)重载:方法名相同,参数列表不同(参数类型不同,或者参数个数不同,甚至是参数顺序不同) (3)重写是运行时的多态性,重载是编译时的多态性
6.实现多重继承的三种方式
(1)直接实现多个接口 (2)扩展(extends)一个类然后实现一个或多个接口 (3)通过内部类去继承其他类
7.接口和抽象类的区别
(1)相同点 ①都不能被实例化 ②接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。 (2)不同点 ①接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现。 ②接口中的成员变量只能是public static final类型,抽象类中的成员变量可以是各种类型 ③实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。 ④接口强调特定功能的实现,而抽象类强调所属关系。
8.为什么要有hashCode?
以HashSet为例,使用HashSet添加一个元素时,会根据HashCode值计算出hash值,进而转换成存储表中的索引值。
9.什么是内部类,内部类的分类有哪些?
(1)在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。 (2)内部类本身就是类的一个属性,与其他属性定义方式一致。 (3)内部类可以分为成员内部类、局部内部类、匿名内部类和静态内部类
10.静态方法和实例方法的区别是什么?
(1)在外部调用静态方法时,可以使用“类名.方法名”,也可以使用“对象名.方法名”;而实例方法只能使用“对象名.方法名”。 也就是说,调用静态方法可以无需创建对象。 (2)静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员和实例 方法;而实例方法无此限制。
11.静态变量和实例变量的区别是什么?
(1)静态变量属于类,在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间 (2)实例变量属于实例对象,每次创建对象,都会为每个对象分配成员变量内存空间
12.构造方法有哪些特性?
(1)名字与方法名相同 (2)没有返回值,但不能用void声明构造函数 (3)生成类的对象时自动执行,无需调用
13.在Java中定义一个不做事且没有参数的构造方法的作用
Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特有的构造方法,则会调动父类中“没有参数的 构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中 特定的构造方法,则编译时将会发生错误。
14.break,continue,return的区别和作用
(1)break:跳出本次总循环,不在执行当前循环体 (2)continue:跳出本次循环,继续执行下次循环 (3)return:程序返回,不再执行下面的代码
15.构造器(constructor)是否可被重写(override)?
构造器不能被继承,因此不能被重写,但可以被重载
16.final、finally以及finalize的区别
(1)final可以修饰类、变量和方法;修饰类表示该类不能被继承,修饰方法表示该方法不能被重写,修饰变量表示 该变量是一个常量不能被重新赋值 (2)finally一般用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码放在finally代码块中,表示 不管是否出现异常,改代码块都会执行,一般用来存放一些关闭资源的代码。 (3)finalize是一个方法,属于Object类的一个方法。当对象被回收时,系统自动调用该方法的finalize方法。子类 可以重写该方法。当某个对象没有任何引用时,jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁 该对象,在销毁该对象前,会先调用finalize方法。垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也 可以通过System.gc() 主动触发垃圾回收机制
17.Java语言有哪些特点(面向对象的四大特性)
封装、继承、多态和抽象
18.什么是Java程序的主类?应用程序和小程序的主类有何不同?
一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类;而在Java小程序中,这个 主类是一个继承自系统类JApplet或Applet的子类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类 是Java程序执行的入口点。
19.深克隆和浅克隆
(1)浅克隆是创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来属性所指向的对象的内存地址。 (2)深克隆是创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java对象复制方式
(1)将 A 对象的值分别通过 set 方法加入 B 对象中 (2)重写 java.lang.Object 类中的方法 clone() (3)工具类BeanUtils和PropertyUtils进行对象复制 (4)通过序列化实现对象的复制,序列化就是将对象写到流的过程,能够实现序列化的对象其类必须实现Serializable接口
20.String类的截取方法
(1)public String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引处的字符开始,直到此字符串末尾。
示例: "unhappy".substring(2) returns "happy"
(2)public String substring(int beginIndex,int endIndex)返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。
实例:
"hamburger".substring(4, 8) returns "urge"
"smiles".substring(1, 5) returns "mile"
String、StringBuffer、StringBuilder的区别
(1)String:不可变字符序列,效率低,但是复用率高(常量池中的数据可以共用) (2)StringBuffer:可变字符序列、(增删)效率较高、线程安全 (3)StringBuilder:可变字符序列、(增删)效率最高、线程不安全
String s = "hsp";与String s2 = new String("hsp");的区别
(1)方式一:先从常量池查看是否有“hsp”数据空间,如果有,直接指向;如果没有,则重新创建,然后指向。 s最终指向的是常量池的空间地址。 (2)方式二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有“hsp”,重新创建; 如果有,直接通过value指向。最终指向堆中的空间地址。
21.如何跳出双重for循环
在最外层for前加个标记,break 标记名; 例如:
label:for(){
for(){
break label;
}}
22.谈一下异常
(1)异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error和java.lang.Exception,平时所说的异常是指java.lang.Exception ①Error(错误):java虚拟机无法解决的严重错误 ②Exception(异常):因编程错误或偶然的外在因素导致的一般性问题,可以针对性的使用代码进行处理。 (2)Exception(异常)又可以分为: ①运行时异常:编译器检查不出来,一般是指编程时的逻辑错误,是程序员应该避免出现的异常 常见的运行时异常有空指针异常、数学运算异常、数组下标越界异常、类型转换异常等 ②编译时异常:运行时异常(RuntimeException)以外的异常,是编译器要求必须处理的异常,如IOException、SQLException等 (3)异常处理方式: ①try-catch-finally ②throws(将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM) (4)自定义异常 自定义异常类名,该类继承Exception或者RuntimeException ①如果继承Exception,属于编译异常 ②如果继承RuntimeException,属于运行时异常。一般来说我们都继承RuntimeException 抛出异常使用:throw new 异常类名(参数);
23.谈一下Java序列化和反序列化,序列化和反序列化为什么要实现Serializable接口?
什么是接口幂等性?为什么会产生接口幂等性问题?如何保证接口幂等性?
什么是接口幂等性?
对于一个接口而言,无论调用了多少次,最终得到的结果都是一样的。防止接口的重复无效请求
为什么会产生接口幂等性问题?
(1)网络波动,可能会引起重复请求 (2)使用了失效或者超时重试机制(Nginx重试、RPC重试或者业务层重试) (3)页面重复刷新 (4)使用浏览器后退按钮重复之前的操作,导致重复提交表单
如何保证接口幂等性?
(1)页面控制:点击之后按钮置灰或者是加载中,避免用户重复点击多次,生成相同的数据 (2)使用PRG(Post/Redirect/Get)模式 ①将表单提交的处理和重定向分离开来。服务端在处理表单提交后,将重定向的URL作为响应返回给客户端,由客户端进行跳转。这样可以确保当用户刷新页面时,浏览器只会发送GET请求,而不是再次提交POST请求 (3)token 机制实现 ①客户端会先发送一个请求去获取 token,服务端会生成一个全局唯一的 ID 作为 token 保存在 redis 中,同时把这个 ID 返回给客户端,Token 最好将其放到 Headers中。 ②客户端第二次调用业务请求的时候必须携带这个 token。 ③服务端会校验这个 token,如果校验成功,则执行业务,并删除 redis 中的 token。 ④如果校验失败,说明 redis 中已经没有对应的 token,则表示重复操作,直接返回指定的结果给客户端。 注意:在并发情况下,执行 Redis 查找数据与删除需要保证原子性,否则很可能在并发下无法保证幂等性。可以使用分布式锁或者使用 Lua 脚本来保证原子性。 (4)生成唯一标识符(UUID):在每次请求接口时,生成一个唯一的标识符,并将其作为请求的一部分发送给服务器。服务器在处理请求前,首先检查该标识符是否已经存在于数据库中,如果存在则表明该请求已经被处理过,直接返回之前的结果即可。如果不存在,则继续处理请求,并将该标识符保存到数据库中。 (5)乐观锁:在数据库中的每个记录中添加一个版本号字段。在每次更新记录时,先检查当前记录的版本号是否与请求中携带的版本号一致,如果一致则执行更新操作,并将版本号+1。如果不一致,则表示该记录已被其他请求修改,返回错误信息。
String s=new String(“abc“) 创建了几个对象?
(1)如果String常量池中,已经创建"abc",则不会继续创建,此时只创建一个对象new String("xyz") (2)如果String常量池中,没有创建"abc",则会创建两个对象 一个是new关键字创建的new Sring();另一个是“abc”对象,abc在一个String常量池中,s这个对象指向这个String常量池。
JVM 如何判断一个对象可以被回收
在 JVM 里面, 要判断一个对象是否可以被回收, 最重要的是判断这个对象是否还在被使用, 只有没被使用的对象才能回收。 (1)引用计数器, 也就是为每一个对象添加一个引用计数器, 用来统计指向当前对象的引 用次数,如果当前对象存在应用的更新, 那么就对这个引用计数器进行增加, 一旦这个引用 计数器变成 0, 就意味着它可以被回收了。这种方法需要额外的空间来存储引用计数器, 但是 它的实现很简单, 而且效率也比较高。不过主流的 JVM 都没有采用这种方式, 因为引用计数器 在处理一些复杂的循环引用或者相互依赖的情况时, 可能会出现一些不再使用但是又无法回收的内存, 造成内存泄露的问题。 (2)可达性分析, 它的主要思想是: ① 首先确定一系列肯定不能回收的对象作为GC root,比如虚拟机栈里面的引用对象、 本地方法栈 引用的对象等, 然后以 GC ROOT作为起始节点,从这些节点开始向下搜索, 去寻找它的直接和 间接引用的对象, 当遍历完之后如果发现有一些对象不可到达,那么就认为这些对象已经没有用了, 需要被回收。 ②在垃圾回收的时候, JVM 会首先找到所有的 GC root, 这个过程会暂停所有用户线程,也就是 stop the world, 然后再从 GC Roots 这些根节点向下搜索, 可达的对象保留, 不可达的就会回收掉。 可达性分析是目前主流 JVM 使用的算法。
Jdk和Jre和JVM的区别
(1) JDK 包含 JRE, JRE包含 JVM。 (1)JDK(Java Develpment Kit):java开发工具,是给开发人员用的,开发必须安装jdk,它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境,以及常用的Java类库等。 (2)JRE(Java Runtime Environment):java运行时环境,用于解释执行Java的字节码文件。提供给运行Java程序的用户来用的,谁需要运行Java程序就需要安装JRE (3)JVM(java Virtual Machine):java 虚拟机,是用来编译解释class文件的,把字节码解释成机器码,让操作系统能够执行。它是整个java实现跨平台的最核心的部分,负责解释执行字节码文件,是可运行java字节码文件的虚拟计算机。所有平台的上的JVM向编译器提供相同的接口,而编译器只需要面向虚拟机,生成虚拟机能识别的代码,然后由虚拟机来解释执行。
为什么 Java 被称作是“平台无关的编程语言”?
(1)Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。 (2)Java 源文件( .java )被编译成能被 Java 虚拟机执行的字节码文件( .class )。 (3)Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
一个http请求分为几个步骤?
(1)构建请求行: 浏览器根据用户提供的URL来构建一个HTTP请求。请求行包括三个部分:请求方法(如GET、POST)、请求的资源路径和HTTP版本号。 (2)查找浏览器缓存: 在真正发送请求之前,浏览器会先检查本地缓存中是否有这个请求的响应结果。如果有且没有过期,那么就直接使用缓存中的数据,否则继续执行下一步。 (3)准备IP地址和端口号: 根据URL中的域名解析出服务器的IP地址,并确定需要连接的TCP端口(默认为80或443)。 (4)等待TCP队列:仅适用于HTTP/1.1,HTTP/1.1允许复用TCP连接以提高性能, 在建立TCP连接之前需要确认当前请求是否需要在建立TCP连接前排队等候。 (5)建立TCP连接: 使用TCP协议与服务器建立连接。这涉及到三次握手过程,确保客户端和服务器之间的通信通道已经准备好。 (6)发送HTTP请求头: 一旦TCP连接建立成功,浏览器将开始发送HTTP请求报文。首先发送的是请求行,接着是一系列的请求头,它们包含了关于请求的各种信息,如Accept、Cookie、User-Agent等。 (7)发送HTTP请求体: 对于POST、PUT等带有请求体的方法,此时浏览器会发送请求体数据。例如表单提交的数据会被编码后随请求一起发送到服务器。 (8)服务器处理请求: 服务器接收到完整的请求后,会对请求进行处理。这可能涉及数据库查询、文件操作或其他业务逻辑。 (9)服务器返回响应: 处理完成后,服务器构建HTTP响应报文并将其通过已建立的TCP连接发送回给客户端。响应报文包含状态行、响应头以及可选的响应体。 (10)关闭TCP连接: 完成数据传输后,通常情况下TCP连接会被关闭。但在HTTP/1.1中,如果请求头指定了Connection: keep-alive,则该连接可能会被保持一段时间以便重用。 (11)浏览器接收响应: 浏览器从TCP连接中读取响应数据,解析响应报文并展示给用户。 (12)更新浏览器缓存: 如果响应头指示了某些内容应该被缓存,浏览器会将其存储起来,以便后续的请求可以直接使用。
TCP/IP五层模型与OSI七层模型的协议
JDK 动态代理和 CGLIB 动态代理的区别是什么?
(1)实现方式:JDK 动态代理是通过反射实现的,而CGLIB动态代理是通过继承目标类来实现的。 (2)目标类限制:JDK 动态代理要求目标类必须要实现接口,而CGLIB动态代理则没有这个限制。 (3)性能:JDK 动态代理相对于 CGLIB 动态代理来说生成的代理类的效率会低一些。 (4)对象类型:JDK 动态代理只能代理实现了接口的类,CGLIB 通过继承实现,不能代理 final 类。 (5)依赖库:JDK 动态代理是 Java 自带的库,不需要额外的依赖,而 CGLIB 动态代理需要依赖 cglib 库。
Iterator是什么?
Iterator接口提供遍历实现任何Collection接口的集合。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合中的Enumeration,并且迭代器允许调用者在迭代过程中移除元素。
Iterator和ListIterator之间有什么区别?
(1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List (2)Iterator只可以向前遍历,而ListIterator可以双向遍历 (3)ListIterator继承Iterator接口,并添加了一些额外的功能,比如添加、替换一个元素,获取前面或后面元素的索引位置
Enumeration和Iterator接口的区别
(1)Enumeration的速度是Iterator的两倍,并且使用更少的内存。Enumeration是非常基础的,也满足了基础的需要。但是,与Enumeration相比,Iterator更加安全;因为当一个集合正在被遍历时,它会阻止其他线程去修改集合。 (2)迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能做到。
Java程序占用 CPU 过高怎么排查
(1)第一步,使用 top 找到占用 CPU 最高的 Java 进程 (2)第二步,用 top -Hp 命令查看占用 CPU 最高的线程
I/O流
流一般需不需要关闭,如果关闭的话用什么方法?一般在哪个代码块里关闭比较好?处理流是怎么关闭的?如果有多个流互相调用传入是怎么关闭的?
(1)流一旦打开就必须关闭,使用close方法关闭 (2)一般放入finally语句块中(finally语句一定会执行) (3)调用了的处理流就关闭处理流 (4)多个流互相调用只关闭最外层的流
Java中有几种类型的流?
(1)按照操作数据单位的不同分为:字节流和字符流 (2)按照流的流向不用分为:输入流和输出流 (3)按照流的角色不同分为:节点流,处理流(包装流)
反射
getFields()和getDeclaredFields()的区别
(1)getFields() 只能获得public的field (2)getDeclaredFields() 可以获得所有的field
反射怎么破坏私有结构去设置值
使用暴破setAccessible(true),可以访问 private 构造器/方法/属性
什么是Socket
在网络编程中,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 Socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
javaweb
cookie和session的区别
(1)cookie存储数据在客户端,session在服务器端 (2)cookie有数据大小限制,session没有 (3)cookie相对于不安全,session安全 (4)cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,session一般失效时间较短,客户端关闭或者session 超时都会失效。