目录
String、StringBuffer、StringBuilder的区别?
Checked Exception和Unchecked Exception的区别?
ClassNotFoundException和NoClassDefFoundError的区别?
Java语言的特点?
面向对象、跨平台、支持多线程、具备异常处理机制和自动内存管理机制、支持网络编程。
JDK、JRE、JVM?
JDK是Java开发工具包,包含了JRE和编写Java程序所需要的工具,比如javac、javadoc、javap等等。JRE是Java运行时环境,包含了运行已编译Java程序所需要的全部内容,主要包括JVM和Java基础类库。JVM是Java虚拟机,是解释和执行Java字节码的地方。
机器码和字节码的区别?
机器码是计算机能直接执行的二进制指令。而字节码是JVM能解释执行的中间代码,也就是扩展名为.class的文件。
八大基本数据类型以及它们对应的引用数据类型?
基本数据类型 | 占用字节数 | 引用数据类型 |
---|---|---|
byte | 1 | Byte |
short | 2 | Short |
int | 4 | Integer |
long | 8 | Long |
float | 4 | Float |
double | 8 | Double |
char | 2 | Character |
boolean | 1或4 | Boolean |
如果boolean是单独使用,则boolean占4个字节。
如果boolean是以boolean数组的形式使用,则boolean占1个字节
讲一下自动装箱和自动拆箱?
自动装箱是将基本数据类型自动转换成对应的引用数据类型,其实就是调用了引用类的valueOf()方法,比如Integer.valueOf()方法。
自动拆箱是将引用数据类型自动转换成对应的基本数据类型,其实就是调用了引用类的value()方法,比如Integer.intValue()方法。
重载和重写的区别?
重载指的是在同一个类中,可以定义多个名称相同但参数列表不同的方法,这些方法根据参数的类型、顺序或数量的不同,可以执行不同的操作。
重写指的是子类重新定义父类中已有的方法,可以用来改变或扩展父类的行为,重写方法具有相同的方法名和参数列表,需要在子类中使用@Override注解来明确指出该方法是重写父类方法。
注:如果父类的返回类型是void或基本数据类型,子类必须相同,如果是引用数据类型,子类<=父类的返回类型;子类>=父类的访问权限;子类<=父类抛出的异常;不能重写私有方法。
面向对象和面向过程的区别?
面向对象是将问题领域中的实体抽象成对象,通过对象之间的合作来解决问题。
面向过程是将解决问题的过程拆成一个个方法,通过一个个方法的执行来解决问题。
面向对象的三大特征是什么?
分别是封装、继承、多态。
封装指的是把一个对象的状态信息隐藏在对象内部,不允许外部对象直接访问对象的内部信息,但是可以提供一些能被外界访问的方法来操作内部信息。
继承允许一个类从另一个类上继承属性和方法。子类继承父类并在父类的基础上进行修改和扩展,提高了程序的可维护性。
多态顾名思义,表示一个对象具有多种状态,具体表现为父类的引用指向子类的实例。
接口和抽象类的共同点和区别?
共同点是都无法被实例化,都可以包含抽象方法,都可以使用default关键字来定义具有默认实现的方法。
区别主要有三点。第一点,接口主要用于对类的行为进行约束,一个类实现了某个接口就具有对应的行为。而抽象类主要用于提高代码的复用性,强调的是所属关系。
第二点,一个类只能继承一个抽象类,但是可以实现多个接口。
第三点,接口中的成员变量默认都是public static final类型的,不能被修改并且必须有初始值。而抽象类的成员变量默认是default类型的,可以在子类中被重新定义,也可以被重新赋值。
注:接口中的方法默认都是public abstract类型的。
浅拷贝、深拷贝、引用拷贝?
浅拷贝会在堆上创建一个新对象,但如果原对象内部的属性是引用类型的话,浅拷贝只会复制内部对象的引用地址,也就是说原对象和拷贝对象共用一个内部对象。
深拷贝也会在堆上创建一个新对象,但深拷贝会完全复制整个原对象,包括原对象所包含的内部对象。
引用拷贝不会创建新对象,它只会复制原对象的引用地址。
Object类的常见方法有哪些?
equals、hashCode、toString、getClass、clone、wait、notify、notifyAll
==和equals的区别?
我先说下==,对于基本数据类型,==比较的是值。对于引用数据类型,==比较的是对象的内存地址。我再说下equals,equals不能用来比较基本数据类型,只能用来比较引用数据类型,如果类没有重写equals方法,则比较的是对象的内存地址,如果类重写了equals方法,一般比较的是两个对象的属性值是否相同,如果相同则返回true,比如String类的equals方法就是重写过的,它比较的是字符串内容而不是对象的内存地址。
hashCode有什么用?
hashCode是用来获取哈希码的,也称为散列码,哈希码可以用来确定该对象在哈希表中的索引位置。
为什么重写equals方法时必须重写hashCode方法?
因为我们要保证equals方法判断两个对象相等时,这两个对象的hashCode值也要相等。如果重写 equals方法但没有重写hashCode方法的话,就可能会导致equals方法判断相等的两个对象,它俩的hashCode值却不相等。
String、StringBuffer、StringBuilder的区别?
String是不可变的,每次对String类型的数据进行修改时,都会创建一个新的String对象,它是线程安全的。
StringBuffer和StringBuilder都继承自AbstractStringBuilder类,它俩都是可变的,都可以通过toString方法转换为String类型的对象。StringBuffer是线程安全的,因为它对方法的调用加了同步锁。而StringBuilder没有加锁,所以它是非线程安全的,不过也正因为没有加锁,所以StringBuilder的性能比StringBuffer要更高。
再说下使用场景,String适用于操作少量的数据,StringBuffer适用于多线程环境下操作大量的数据,StringBuilder适用于单线程环境下操作大量的数据。
String为什么是不可变的?
因为String类源码中的字符数组是被private final修饰的,并且Stirng类并没有暴露修改这个字符数组的方法,这意味着我们不能修改String对象内部的字符数组。还有就是String类自己是被final修饰的,这意味着它不能被子类继承和修改,进一步确保了String对象的不可变性。
字符串常量池了解吗?
字符串常量池是JVM为了提升性能和减少内存消耗,专门针对String类开辟的一块区域,主要是为了避免字符串的重复创建。
Integer常量池的大小?
-128~127
Java的异常结构?
在Java中,所有的异常都有一个共同的祖先,那就是java.lang包中的Throwable类。Throwable类有两个重要的子类Exception和Error,Exception是程序本身可以处理的异常,可以被catch捕获,Exception又分为Checked Exception受检查异常和Unchecked Exception不受检查异常。Error是程序无法处理的错误,比如VirtualMachineError虚拟机运行错误、OutOfMemoryError内存溢出错误、NoClassDefFoundError类定义错误等等,这些错误发生时,JVM一般会选择终止线程。
Checked Exception和Unchecked Exception的区别?
Checked Exception是受检查异常,Java代码在编译过程中,如果受检查异常没被catch捕获或者没被throws关键字处理的话,就无法通过编译。除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有FileNotFoundException、ClassNotFoundException、SQLException等等。
Unchecked Exception是不受检查异常,Java 代码在编译过程中,我们即使不处理不受检查异常也可以正常通过编译。RuntimeException及其子类都是不受检查异常,常见的不受检查异常有NullPointerException、IllegalArgumentException、NumberFormatException、ArrayIndexOutOfBoundsException、ClassCastException等等。
ClassNotFoundException和NoClassDefFoundError的区别?
ClassNotFoundException是一个受检查异常,当程序试图通过完全限定的名称去加载一个类,但在类路径找不到它的定义时,就会抛出这个异常,通常发生在使用Class.forName()、ClassLoader.loadClass()这几个方法加载类的时候。
NoClassDefFoundError是一个错误,当Java虚拟机在编译时能找到这个类的定义,但在运行时找不到这个类的定义时,就会抛出这个错误,一般缺少依赖的jar包或定义的类丢失都会导致这个问题。
try-catch和throw和throws的区别?
try-catch用于捕获和处理异常,try里面放置可能抛出异常的代码,当异常发生时,程序会跳转到对应的catch中执行异常处理逻辑,catch可以处理特定类型的异常或者是它的父类异常。
throw用于手动抛出异常,在方法中使用throw关键字,可以主动将特定的异常抛给上层方法。
throws用于声明方法可能抛出的异常类型,添加在方法签名的尾部,它不会处理异常,也是将异常抛给上层方法。
详解try-catch-finally注意点?
try是必须的,catch和finally是可选的,但catch和finally必须存在一个或者都存在;
如果try或者catch中有return语句而finally没有return语句,那么try或者catch的return语句执行完后不会立即结束函数,而是将结果保存到栈帧的局部变量表中,然后再执行finally块中的代码。
如果finally中有return语句,那么会直接返回,不会执行try或者catch的return语句;
每个异常只能被catch一次。
泛型是什么?
泛型就是在定义类、接口、方法时不指定具体的参数类型,而是在使用它们时才指定具体的参数类型,会在编译期去检查参数类型是否正确,增强了代码的可读性和稳定性。
反射是什么?
反射指的是在程序运行时动态地获取和操作类、方法、属性等信息的能力,不需要在编译时确定这些信息,使我们的代码更加灵活。
缺点:反射的性能较低,因为需要在运行时动态查找和调用,所以性能不如直接调用。反射无法在编译期进行静态类型检查,只能在运行时发现错误,所以存在安全风险。
反射在框架中的应用?
Spring:
1. 依赖注入,Spring框架在应用启动时会扫描类路径下的所有组件,并使用反射来检测类中的注解信息,当发现类中标有@Autowired注解时会使用反射获取该属性的类型信息,并通过反射机制创建该类型的实例。
2、AOP,Spring框架在应用启动时会扫描类路径下的所有组件,并使用反射来检测类中的注解信息,当发现类带有@Aspect注解时会使用反射动态地创建该类的代理对象,然后代理对象会在目标方法的执行前后织入切面的通知。
反射获取Class?
调用实例对象的getClass方法:Class userClass1 = new USer().getClass();
类名.class:Class userClass2 = User.class;
调用Class.forName方法传入类路径:Class userClass3 = Class.forName("com.jun.entity.User");
API和SPI的区别?
一般模块之间都是通过接口进行通讯,服务调用者通过调用接口来获取服务提供者的提供的能力,它俩的主要区别就是接口的定义者不同。在API中,接口的规则是由服务提供者定义的。而在SPI中,接口的规则是由服务调用者定义的。
我举个简单的例子解释下,如果一家公司既负责设计芯片又负责制作芯片,那么这种情况就属于API。而如果一家公司只负责设计芯片,定义好这个芯片的标准,由其他公司按照标准制作并交付具有自家特色的芯片,那么这种情况就属于SPI。
静态代理和动态代理的区别?
在静态代理中,我们对目标对象中每个方法的增强都是手动完成的,需要对每个目标类都单独编写一个代理类。如果接口中新增加了方法,那么目标类和代理类都要进行修改,非常不灵活。
而动态代理是比较灵活的,不需要对每个目标类都创建一个代理类,甚至不需要目标类实现了接口,可以直接代理目标类。动态代理是在运行时动态生成类字节码并加载到JVM中的。
值传递和引用传递的区别?
对于值传递,方法接收的是实参值的拷贝,会创建副本,修改形参不会影响实参。
对于引用传递,方法接收的是实参所引用的对象在堆中的地址,不会创建副本,修改形参会影响实参。
Java是值传递还是引用传递?
在Java中只有值传递,如果参数是基本类型的话,传递的就是基本类型字面量值的拷贝,会创建副本。如果参数是引用类型的话,传递的就是实参所引用的对象在堆中地址值的拷贝,也会创建副本。
static和final的区别?
static用于定义静态成员变量、静态方法和静态代码块,静态成员变量和静态方法不依赖于类的实例,可以通过类名直接调用,静态成员变量在内存中只有一份,被所有实例共享,静态方法只能访问静态成员变量和静态方法,不能访问非静态的。静态代码块在类加载时就会执行里面的代码,常用来初始化静态资源。
再说final,被final修饰的类时不能被继承的,并且这个类中的所有成员方法都会被隐式地指定为final方法,被final修饰的方法是不能被重写的,被final修饰的变量是基本类型的话,值是不能被修改的,被final修饰的变量是引用类型的话,是不能再指向其它对象的。当希望某个类、方法或变量具有不可变性时,可以使用final修饰,安全性较高。
Java8的新特性主要有哪些?
第一个,可以使用default关键字在接口中定义具有默认实现的方法,使得我们向现有接口中添加新方法时,不会破坏已有的实现类。
第二个,提供了Lambda表达式,允许使用更简洁的代码去编写匿名函数,使编写过程更加方便。
// forEach遍历集合
list.forEach((item) -> System.out.println(item));
map.forEach((k, v) -> System.out.println(v));
// 排序,list排序用Collections,数组排序用Arrays
Collections.sort(list, (Integer o1, Integer o2) -> o1 - o2);
Arrays.sort(array, (Integer o1, Integer o2) -> o1 - o2);
// 新建线程
new Thread(() -> System.out.println("Hello World")).start();
// Lambda表达式在Stream API中也扮演着重要的角色,可以使用更简洁的代码处理集合数据
list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
list.stream().map(s-> s.length()).collect(Collectors.toList());
第三个,提供了用于处理集合数据的Stream API,可以通过流式操作的方式,对集合进行过滤、映射、排序、遍历、收集等一系列操作,常用的有filter、map、sorted、forEach、collect。
执行顺序:filter -> map -> sorted -> forEach/collect,forEach和collect方法是终止操作,它俩不能放在一起使用。
第四个,提供了一些新的用于操作日期和时间的类,比如LocalDate、LocalTime和LocalDateTime,位于java.time包中,使用起来比原来的Date类更加方便。
// 将字符串转为时间
LocalDate.parse("2024-5-12");
LocalTime.parse("12:12:22");
LocalDateTime.parse("2024-5-12 12:12:22");
// 日期计算
LocalDate.now().plusDays(1);
LocalTime.now().plusHours(1)(1);
LocalDateTime.now().plusDays(1);
// 获取本月的第一天
LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
想起来的就这么多。
Java中一个对象占用的字节数?
对象在内存中的存储布局分为3块区域,分别是对象头、实例数据和对齐填充。对象头中包含8字节的MarkWord和4字节的KClassWord,一共占用12个字节,如果是数组对象的话,对象头中还会存储一个数组长度,此时对象头占用16个字节。
如果对象头和实例数据占用的字节数不是8的倍数的话,会进行对齐填充保证对象最终占用的字节数是8的倍数,比如我们创建一个没有任何属性的空对象,它的对象头占用12个字节,实例数据占用0个字节,然Java会给它填充4个字节保证最终字节数为16。
引用地址占4个字节。
URI和URL的区别?
URI是统一资源标志符,可以用来唯一地标识互联网上的任何资源,它包括URL和URN两个类别。
URL是统一资源定位符,它是一种具体的URI,不仅可以用来唯一地标识互联网上的任何资源,而且还提供了路由到这个资源的方式。
URN是统一资源名称,如果说某个人的家庭住址是URL的话,那么URN就相当于这个人的身份证号。
为什么浮点数运算的时候会有精度丢失的风险?
因为浮点数在计算机中是使用二进制表示的,而且在表示一个数字时宽度是有限的,如果小数对应的二进制是无限循环的,那么只能被截断,这时就会导致小数的精度丢失。
浮点数精度解决方案?
使用BigDecimal类不会造成精度丢失,它的底层使用了一个BigInteger对象存储数据,并且使用了一个int类型的变量指定小数点的位置。
Java为什么可以跨平台?
因为不同平台上都有相应的Java虚拟机实现,所以Java程序可以在任何支持Java虚拟机的平台上运行。