文章目录
- 1. 基础篇
- 1、JDK和JRE的区别
- 2、说一说方法的值传递与引用传递
- 3、== 和equals的区别
- 4、final关键字的作用
- 5、关于Math的三个取整方法
- 6、String str="Hello"和 String str= new String("Hello")的区别
- 7、String及StringBuffer类常用方法有哪些(至少10个)?
- 8、new String("Hello") + new String(World)会创建几个对象?
- 9、说一说接口与抽象类区别?
- 10、关于类的初始顺序
- 11、HashMap的底层原理
- 12、JDK实现定时任务?
- 13、说一说Java内存模型JMM
- 14、synchronized和volatile的区别
- 15、说一说类加载流程
- 16、说一说一个对象有多大
- 17、说一说Java的类加载器
- 18、如何实现深拷贝和浅拷贝
- 19、可见性、缓存一致性协议、总线锁
- 20、Java常见异常有哪些?
- 21、Java常用注解有哪些?
- 22、注解的底层原理是什么?
- 23、常用集合类有哪些?
- 24、ArrayList与LinkedList的区别?
- 25、常用IO类有哪些?分为几类?
- 26、什么是序列化与反序列化?底层如何实现的?
- 27、什么是反射? 反射相关的类有哪些? 反射创建对象的几种方式?
- 28、String类为什么是不可改变的?设计为不可变对象有哪些好处?
- 2. 并发篇
- 3. JVM篇
1. 基础篇
1、JDK和JRE的区别
- JDK:Java Development Kit即Java开发工具包,包含了JRE和其它工具比如:java、javac、javap等等;
- JRE:Java Runtime Environment即Java运行环境,运行Java程序所必须的环境;
- JDK包含JRE,JDK目录下有一个jre目录,jre中的bin目录下就是各种java开发工具和jvm;
示例图:
2、说一说方法的值传递与引用传递
- 值传递就是仅把实参的值传递给形参,改变形参不会对实参造成影响;
- 引用传递就是传递地址值,变量指向同一内存地址,好比C语言的指针变量,改变形参,实参也会跟着改变;
- java中只有值传递,不存在引用传递;对于基本类型,传递的是栈中的值;对于引用类型,传递的是栈中的地址值;
- 当形参和实参就指向同一对象,故修改形参,实参也被修改了,产生引用传递的假象;
- 但当形参是String类型时,由于String类是不可修改的,每次对String的修改都和导致新对象的产生,所以形参和实参并不是指向同一对象,故改变形参不会对实参造成影响;同样Integer、Float等都是这样。
示例代码:
/**
* Person实体类
* @date 2022-08-19
* */
public class Person {
public int age;
String name;
boolean sex;
}
//set方法
public void set(Person person){
person.age = 20;
}
public void set(String str){
str="World";
}
/**
* 测试
* */
@Test
public void mt(){
//普通类型
Person person = new Person();
//String类型
String str = new String("Hello");
//调用set方法
set(person);
set(str);
//被修改为20了
System.out.println(person.age);
//依然是Hello
System.out.println(str);
}
3、== 和equals的区别
- 对于基本类型,==比较的是值;对于引用类型,==比较的是栈中的地址;
- 没重写equals方法的话,两者等价;
- 重写了就按重写的比较规则,如String中比较的就是字符串的内容是否相同;equals不能用于基本类型的比较;
示例代码:
/**
* 未重写的equals就是==
* */
public boolean equals(Object obj) {
return (this == obj);
}
/**
* String中重写的equals(): 比较的就是内容是否相同
* */
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
4、final关键字的作用
- 修饰类:该类不能被继承;
- 修饰方法:该方法不能被子类重写;
- 修饰引用类型:该引用不能再次指向其他对象,即地址值不能改,但对象内容可以修改;
- 修饰基本类型:表示定义一个常量;成员变量必须立刻赋值,静态成员变量可以在静态代码块中赋值,局部变量只能赋一次值,还可以修饰形参,该形参只能赋值给其他变量,不能改变;
示例代码:
//final修饰类,该类不能被继承
public final class F {
//成员变量,必须赋值
final int num = 10;
final static int a;
//静态代码块中就必须赋值
static{
a = 10;
}
/**
* final修饰的方法,子类不能重写
* */
public final void finalTest(final int num , final String str){
//修饰局部变量,只能赋值一次
final int m;
m = 10;
//不能再次修改
//m = 20;
int n = num;
//不能修改
//num = 10;
String s = str;
//str = "Hello";
}
}
5、关于Math的三个取整方法
- ceil():向上取整,取整后大于取整前的值;
- floor():向下取整,取整后小于取整前的值;
- round():四舍五入;
示例代码:
/**
* Math中的三种取整
* */
@Test
public void MathTest(){
//ceil: 向上取整
System.out.println(Math.ceil(3.14));//4
System.out.println(Math.ceil(-3.14));//-3
//floor: 向下取整
System.out.println(Math.floor(3.14d));//3
System.out.println(Math.floor(-3.14d));//-4
//round: 四舍五入
System.out.println(Math.round(3.14f));//3
System.out.println(Math.round(-3.14f));//-3
System.out.println(Math.round(-3.55f));//-4
}
6、String str="Hello"和 String str= new String(“Hello”)的区别
- 对于String str = “Hello”,若常量池中存在"Hello",就将常量池中"Hello"对象的地址赋值给str;若常量池中没有"Hello",就会创建一个"Hello"对象放到常量池;并将常量池"Hello"的地址赋值给str;始终指向常量池;
- 对于String str = new String(“Hello”),若常量池存在"Hello",就不在常量池中放入"Hello",仅在堆中创建"Hello"对象,并指向堆中的对象;若常量池中不存在"Hello",就在常量池中和堆中都创建"Hello";指向的堆中的"Hello"对象;始终指向堆;
示例代码:
/**
* 关于String str = "Hello"和String str = new String("Hello")区别
* */
@Test
public void str(){
String str1 = "Hello";
String str2 = "Hello";
//为true,都指向常量池中的“Hello”
System.out.println(str1 == str2);
//false, String的改变会导致新对象的产生
System.out.println((str1+"World") == (str2+"World"));
String str3 = new String("Hello");
//false,str3指向堆中的对象
System.out.println(str1 == str3);
String str4 = new String("Hello");
//false
System.out.println(str3 == str4);
}
7、String及StringBuffer类常用方法有哪些(至少10个)?
String常用方法:
charAt()、
compareTo()、
compareToIngoreCase()、
concat()、
contains()、
copyValueOf()
endsWith()、
equals()、
equalsIgnoreCase()、
format()、
getBytes()
getChar()、
join()、
length()
replace()、
substring()
trim()
StringBuffer常用方法:capacity()、append()、charAt()、delete()、codePointAt()、indexOf()、insert()、replace()、substring()、toString()
链接:String、StringBuffer、StringBuilder参考手册
8、new String(“Hello”) + new String(World)会创建几个对象?
- 假设常量池中没有"Hello"、“World”、“HelloWorld”:则会创建6个对象
- +符号表示字符串拼接,底层是创建StringBuilder对象调用append()方法追加,然后用toString()方法返回拼接后的字符串;即:
new StringBuilder().append("Hello").append("World").toString();
- 堆和常量池中"Hello":x2
- 堆和常量池中的"World":x2
- StringBuilder对象:x1
- 堆中的"HelloWorld"对象:x1
如果常量池中有相应对象,则减去即可;
9、说一说接口与抽象类区别?
浅层次:
- 两者都不能创建实例对象;
- 接口和抽象类都可以没有抽象方法;
- 类上都不能用final修饰,不然就没有意义了;
- 接口用关键字interface定义,抽象类用关键字abstract定义
- 接口中的变量默认且只能是public static final,方法默认且只能是public,而抽象类可有各种类型变量、方法;
- 接口中是静态方法和default方法有方法体,而抽象类中是普通方法有方法体;
- 一个类只能继承一个抽象类,但可以实现多个接口,实现多重继承;
- 接口中不能有构造方法,抽象类中可以有;
深层次:
- 接口设计目的是对类行为有无的约束,是一种 ‘like a’ 的关系;
- 抽象类的目的是代码复用,公共部分就是抽象类中普通方法;抽象方法就留给子类各自去实现;是一种’is a’的关系;
- 设计接口比设计抽象类更容易;不用考虑特别全面;因为一个类可以实现多个接口;抽象类一般是先有子类,当发现某些类有较多相同的方法时,就抽象出一个抽象类,代码复用;
示例代码:
/**
* 关于接口
* @author jgy
* @date 2022-08-19
*/
public interface InterfaceTest {
//必须赋值,因为默认是public static final
int num = 10;
String s = "h";
//其实还是public static final的,因为必须赋值
public int a = 10;
//报错,a是public static final类型的,属于静态常量
//public void setA(int a){
// this.a = a;
//}
//public可以不用写,因为默认都是public
public void myMethod();
//protected和private修饰都不行
//protected void myMethod2();
//default方法,必须有方法体,默认都是public
default void myMethod3(){
System.out.println("default方法,必须有方法体");
}
//静态方法,public
static void myMethod4(){
System.out.println("静态方法,必须有方法体");
}
}
抽象类和接口都不能实例化对象,只能继承一个抽象类,可以同时实现多个接口。
接口可以说是纯抽象类,接口中可以有抽象方法,JDK9及以后可以有default修饰的实现了的方法以及静态方法;变量全是public static final修饰的变量即静态常量。不能有构造方法。
抽象类中可以有抽象方法、实现的方法、变量、常量、构造方法,有抽象方法,就必须声明为抽象类(abstract),abstract修饰的类不一定有抽象方法。即有抽象方法就一定是抽象类,抽象类不一定有抽象方法。
10、关于类的初始顺序
一、初始化所涉及到的内容:5样
1、静态代码块、变量:子类、父类的
2、非静态代码块:子类的、父类的
3、构造方法: 子类的、父类的
4、成员变量:你new对象时才会被初始化
5、局部变量:不会自动初始化,需要先手动初始化后才能使用。
二、初始化的执行顺序
1、父类静态(静态代码块或静态变量谁写在前谁先执行)
2、子类静态(静态变量、静态代码块谁写在前谁先)
3、父类非静态、创建其他类对象优先级相同
4、父类构造
5、子类非静态、创建其他类对象优先级相同
6、子类构造
7、main()方法
8、非静态成员变量与方法是new对象时才初始化
9、静态代码块或静态变量只会初始化一次,成员变量会默认初始化为:0、0.0、’\0’、false、null。
示例代码:
/**
* 关于初始化顺序:
* @author jgy
* @date 2022-07-26 17:06
*/
public class StaticInitialization {
/** 当创建StaticInitialization对象时才会执行 */
{
System.out.println("StaticInitialization非静态代码");
}
//第一执行: 静态变量和静态代码块优先级相同,谁现在前谁先执行
static{
System.out.println("StaticInitialization的静态代码");
}
//第四执行
public static void main(String[] args) {
System.out.println("main()");
cupboard.otherMethod(1);
}
/** 先初始化静态变量,再执行main()方法!! */
/** 第二执行 */
static Table table = new Table();
/** 第三执行 */
static Cupboard cupboard = new Cupboard();
}
/** 碗 */
class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
}
/** 餐具 */
class Tableware {
static Bowl bowl7 = new Bowl(7);
static {
System.out.println("Tableware静态代码块");
}
Tableware() {
System.out.println("Tableware构造方法");
}
/** ①和②优先级相同: 谁写在前谁先执行*/
{
System.out.println("Tableware非静态代码块");//①
}
Bowl bowl6 = new Bowl(6);//②
}
class Table extends Tableware {
//①②③同一优先权,谁写在前谁先执行
{
System.out.println("Table非静态代码块_1");//①
}
Bowl bowl5 = new Bowl(5);//②
{
System.out.println("Table非静态代码块_2");//③
}
/** 静态变量和静态代码块是谁写在前谁就先执行 */
static Bowl bowl1 = new Bowl(1);
static {
System.out.println("Table静态代码块");
}
Table() {
System.out.println("Table构造方法");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard extends Tableware {
/** 非静态代码块 */
{
System.out.println("Cupboard非静态代码块");
}
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard构造方法");
}
void otherMethod(int marker) {
System.out.println("otherMethod(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
11、HashMap的底层原理
1、HashMap是以键值对形式存储数据,键值可以为null,但键必须唯一,内部元素是无序的;
2、HashMap线程不安全,HashTable是线程安全的,但一般用ConcurrentHashMap保证线程安全;
3、HashMap基于Hash表,底层采用数组+链表+红黑树,当链表大于8时会转为红黑树,小于等于6时退化为链表;内部是Entry结点;
4、扩容机制:扩容因子默认是0.75,2倍扩容;即数据达到0.75后容量扩容到原来的两倍;
12、JDK实现定时任务?
实现定时任务有:
- Java自带的Timer、ScheduledExecutorService
- Spring的主类上添加@EnableScheduling,方法上加@Scheduled
- 用Redis的Zset实现,通过 ZSet 实现定时任务的思路是,将定时任务存放到 ZSet 集合中,并且将过期时间存储到 ZSet 的 Score 字段中,然后通过一个无线循环来判断当前时间内是否有需要执行的定时任务,如果有则进行执行
示例代码
13、说一说Java内存模型JMM
包含:程序计数器、方法区、虚拟机栈、本地方法栈、堆;
14、synchronized和volatile的区别
1、volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好;volatile只能修饰变量,而synchronized可以修饰方法,代码块。随着JDK新版本的发布,synchronized的执行效率也有较大的提升,在开发中使用synchronized的比率还是很大的。
2、多线程访问volatile变量不会发生阻塞,而synchronized可能会阻塞。
3、volatile能保证数据的可见性,但是不能保证原子性;而synchronized可以保证原子性,也可以保证可见性。
在JVM手册中,当多线程写被volatile修饰的共享变量时,有两层语义。
1)该变量【立即刷新到主内存】。
2)使其他线程的共享变量立即失效。言外之意当其他线程需要的时候再从主内存取。
在上述案例中,如果c为一个布尔值并且被volatile修饰,那么当线程AB同时更新共享变量c时,此时c对于工作内存AB是可见的。
4、关键字volatile解决的是变量在多个线程之间的可见性;synchronized关键字解决多个线程之间访问公共资源的同步性。
15、说一说类加载流程
类加载过程分为:加载、连接、初始化、使用、卸载;其中,连接分为验证、准备、解析;
16、说一说一个对象有多大
Object Header(头信息)
Class Pointer(指针)
Fields(字段属性)
区分对象与对象的引用:对象是抽线数据类型,对象的引用是指针,指向对象。
对象的结构:对象头、对象数据和对齐填充部分。
对象头:
MarkWord:用于对象运行时数据,如HashCode、锁状态标志、GC分代年龄、线程持有的锁、偏向线程ID、偏向时间戳等。64位操作系统占8字节,32位占4字节
元数据指针:对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。开启指针压缩占4字节,未开启占8字节。
数组长度(数组对象特有):这是数组对象才有的部分,占4字节。
对象数据:
程序中定义的各种类型的字段内容,包括从父类继承的。
对齐填充部分:
由于规定对象的大小必须是8字节的整数倍,未满8字节部分就需要填充。
对象属性的内存分配:
方法体内定义的,就在栈上分配;
类的成员变量,在堆上分配的;
基本类型就严格按基本类型进行内存分配(默认开启指针压缩,不足4字节按4字节填充)。引用类型就是指针占内存(默认4字节,不开启指针压缩就是8字节)
17、说一说Java的类加载器
18、如何实现深拷贝和浅拷贝
当对象A中包含对象B时(或更多其他对象C、D、E……),现在拷贝对象A:
浅拷贝:就是为对象A分配新的内存,将A对象全部拷贝一份,但B对象只拷贝引用值,该引用指向B对象;不会为B对象分配新内存。
深拷贝:就是A、B对象都分配了新内存来存放拷贝A、B对象;
实现深拷贝:A、B对象的类均实现Cloneable接口,重写clone()方法,在clone方法内把B对象(或其他更多的对象)也clone一份。
示例代码
19、可见性、缓存一致性协议、总线锁
CUP>高速缓存>内存
20、Java常见异常有哪些?
21、Java常用注解有哪些?
22、注解的底层原理是什么?
23、常用集合类有哪些?
24、ArrayList与LinkedList的区别?
- 数据结构不同:ArrayList底层基于数组,LinkedList底层基于链表;
- ArrayList可以根据下标随机读取,LinkedList只能从头开始遍历;
- ArrayList更适合查询数据,LinkedList更适合插入和删除数据;
- ArrayList和LinkedList都实现了List接口,但LinkedList还实现了Duque接口,可用来做队列;
25、常用IO类有哪些?分为几类?
常见IO类
26、什么是序列化与反序列化?底层如何实现的?
序列化就是把对象转换为二进制字节码保存,反序列化就是将二进制字节码重新转为对象。底层就是通过IO流来将对象转为二进制,便于传输与保存对象;对象的序列化需要有一个序列化版本号;
27、什么是反射? 反射相关的类有哪些? 反射创建对象的几种方式?
反射就是在运行状态下,可以获取任何一个类的所有属性和方法,并且可以调用这些属性和方法。这种动态获取类的属性和方法的机制就称为反射。
反射相关的类主要有Class类、和发射包reflect中的Method
除了用new创建对象外,还可以用反射创建对象反射创建对象
28、String类为什么是不可改变的?设计为不可变对象有哪些好处?
原因:
通过看String的源码,String的底层是char类型的数组,数组是引用对象,为了防止数组可变,于是加了final修饰,但加final修饰只能保证数组的引用不可变,保证引用不能再指向其他对象,并不能保证数组中的内容不可变,于是又加上private修饰,由
private final修饰的变量,一经初始化就不可改变。最后,String类上也加上了final,避免通过继承String类来破坏。关键是final和private作用再value[]上!!
好处:
多线程下更安全、这样就可使用常量池来节省空间
1、JAVA中的值传递:方法操作成员变量有两种:
一是参数传递:
Java中只有值传递。值传递就是将实参复制一份,将复制的副本传给形参;方法调用中不管参数是基用类本类型还是引型,都是值传递;基本类型将实参的副本传给形参;引用类型是将实参栈中的地址值拷贝一份传给引用类型形参,形参和实参就指向同一对象了,传入的是哪个对象的引用,哪个对象的成员变量就被改变。(String、Integer的不会,因为会new一个新的对象,形参就不是和实参指向同一个对象了),方法调用结束后会,拷贝的对象会被销毁。参数传递相当于实参给形参初始化,后面就没实参什么事了。
二是直接改变成员变量的值:this.name
不存在值的传递,直接把成员变量放到方法里操作,直接改变原始成员变量的值;如构造方法、set方法;哪个对象来调用该方法,哪个对象里的成员变量就被改变。相当于变量前加了this关键词,代表本对象调用。
4、final、finally、finalize的区别?
final:它是Java中的修饰词,可修饰类、方法、成员变量、局部变量
修饰类表示该类不能被继承、修饰方法表示该方法不能被重写;修饰变量表示变量值不能改变,即定义一个常量,如果final修饰的是引用,引用就不能改变,但引用指向的对象里的内容可以改变;final还可以修饰方法的参数。
finally:用于try-catch-finally语句块中的最后,finally中的代码不管是否产生异常都会执行执行,因为在执行try或catch中的return语句前都会先去执行finally中的,finally里不要写return语句,会覆盖try或catch里的return语句。一般用来做善后工作,关闭、释放资源。
finalize:Object类中的finalize()方法,虚拟机回收一个对象前会调用该方法,只会被虚拟机调用,我们主动去调用它不会产生任何效果。
5、什么是泛型?从源码解释下泛型。
泛型就是类型参数化,使用泛型时可以用通配符?、设置泛型上限(extends)、设置泛型下限(super)、可以传入具体类型如List(强调作用);
但泛型只存在代码编译阶段,进入JVM后类型就是具体的了,不存在泛型了,这就是泛型擦除;但如果是有限制的泛型,这个限制还是存在的。
6、说说JAVA的异常体系,是如何处理异常的?
Java有3类错误:语法错误、语义错误、逻辑错误;异常类解决的是语义错误;
顶层是Throwable类,两直接子类Error与Exception。错误程序无法恢复,而异常程序可以恢复,常见的是运行时异常和IO异常,我们可以用try-catch语句捕获异常,可以throws抛出异常;还可以用throw抛出局部异常(throw只能单独抛出一种)。
7、说说JAVA中的控制访问符:
Java中共有public、protected、默认不写、private共4种访问权限来修饰类、方法、和变量。
public修饰的在所有包中均可访问、protected只在同一包或其他包的子类可访问、不写只能在同一包下可访问、private修饰的只能在本类中访问。子类只能继承父类的非私有权限,若父类、子类不在同一包中,父类中默认修饰的成员也不能被继承。
8、关于成员变量与局部变量的初始化:
成员变量和局部变量都需要初始化,只不过成员变量是自动初始化为0或null,而局部变量必须手动初始化后才能使用。
成员变量属于对象,在类的加载过程为:加载→验证→准备→解析→初始化,虚拟机会在堆中给成员变量分配存储空间,自动初始化。
局部变量属于方法,代码运行时方法是放在运行时数据区的栈贞中,而每一个栈贞中都存放着局部变量表、操作数栈、方法出口、动态链接等。因为局部变量表所需的内存空间是在编译期间就完成固定分配的,所以当调用方法前即进入栈贞前,就必须确定这个方法栈贞需要分配多大的变量空间,在方法运行期间就不会改变局部变量表的大小了。
9、String、StringBuffer、StringBuilder的区别
String是字符串常量,final修饰的,不能修改,修改会导致新的对象生成;而StringBuffer和StringBuilder是字符串变量,值是可以改变的;
String、StringBuilder是线程不安全的,而StringBuffer是线程安全的,因为用synchronized修饰了的。
执行速度上StringBuilder>StringBuffer>String.
10、说说接口与抽象类,以及它们的区别?
11、什么是内存泄露与内存溢出?
内存泄漏:不再使用的对象或者变量没有办法回收,这些没用的对象和变量还一直占用着系统的内存空间。内存泄漏到一定程度可能就会导致内存溢出,即内存不够了。
内存溢出:就是当我要申请一块内存的时候,但是此时虚拟机不能够给我提供这么大的内存,就说明内存溢出了,比如出现OutOfMemoryError,StackOverflowError 这种错误信息的时候。
12、说说JAVA类的加载过程:
1、类的生命周期 <==>加载进JVM内存到卸载出JVM内存;
2、经历7步:加载、验证、准备、解析、初始化、使用、卸载;
3、类加载过程<==>前五步。
加载:类全限定名获取二进制字节流;转为方法区里运行时数据结构;创建Class对象;
验证:验证字节码是否符合JVM规范里的全部约束要求;
准备:为类变量分配内存、初始化值,准备阶段值均为“零值“;
解析:符号引用替换直接引用;
初始化:就是执行类构造器()方法的过程。()方法是javacz自动生成的;
13、说说JDK的动态代理?
代理的作用:
代理是一种设计模式,代理就是去扩展或修改主业务,让主业务程序更纯粹。主业务可看作是公共代码,而代理主业务就是为了在主业务继承上增加其他复杂、繁琐的功能。
基于JDK的动态代理:核心:proxy类、InvocatiionHandler类
通过代理接口从而代理一类类;核心是代理类去实现InvocationHandler类,重写incoke()方法,已经invoke()方法三个参数的含义。
基于cglib的动态代理:直接动态代理具体的类;
15、switch中参数可以用String类型吗?
在Java5以前只能是byte,short,char,int类型(或其包装类)的常量表达式。从Java5开始,java中引入了枚举类型,即enum类型。
从Java7开始可以是String类型。但是long在所有版本中都是不可以的。
jdk1.7并没有新的指令来处理switch的string参数,而是通过调用switch中string.hashCode,将string转换为int从而进行判断。hashcode返回的是int型。
16、JAVA中各个类型占用的字节数?
4类8种基本数据类型:boolean(1字节,JVM种按int类型4byte处理)、char(2字节),byte(1字节)、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节);
17、MySQL中有哪些数据类型?各占几字节?
数字类:tinyint(1字节)、smallint(2字节)、int(4)、bigint(8)、float(4)、double(8)
字符串类:char(自定义大小)、varchar(自定义大小)、text、blog
当然在使用varchar 和char类型的时候其实也是有点区别的,因为varchar是变长类型的,所以他会多用一个字节进行存储字符串本身的长度,所以当定义的varchar(10)真实的值不超过10的话,也不会去进行再前面补0,VARCHAR 类型可以根据实际内容动态改变存储值的长度,所以在不能确定字段需要多少字符时使用 VARCHAR 类型可以大大地节约磁盘空间、提高存储效率。而TEXT主要是用来存储大量的字符数据的,
blog主要是用来存储二进制数据的,比如把一张图片转成二进制数据,然后存储到MySQL中去。
时间类:date、timedate、datestamp、timeDate,date占用3个字节,并且只能精确到天,它支持的时间范围为’1000-01-01’到’9999-12-31’。DateTime占用8个字节,他可以支持到具体的秒级别的,它支持的范围为’1000-01-01 00:00:00’到’9999-12-31 23:59:59’。TimeStamp占用4个字节,可以支持到具体的秒级别的,但是他支持的范围相比于datetime的话会少很多,它支持1970-01-01 00:00:00 到 2037年之间。(这个有分时区问题)Time占用3个字节。
18、重载与重写的区别?
方法的重载发生在同一个类中,方法重载就是方法名相同,参数列表不同(参数的个数、类型、顺序),与返回类型无关。
方法的重写在是发生在子类与父类上或实现接口上,子类重写父类的方法,方法名、参数列表、返回类型要完全相同。
19、接口中的静态方法与default修饰的方法?
首先接口中有个默认方法是为了满足一种场景,如果当我们一个现有的业务接口中需要增加一个新的功能的时候,我们需要重新定义一个接口,然后分别在不同的实现类中进行实现,要是实现类很多的话,逐个去实现就会变的跟麻烦。
所以在java8中添加一个默认的方法就是为了解决这种场景的,如果你想增加一个新的功能的话,只需要在接口中定义一个默认的default 来进行修饰的方法,default修饰的方法不能是抽象方法,必须要有方法体。
20、说说不同版本字符串调用intern()方法的区别
常量池在方法区中,JDK1.6前,当new了一个字符串后,当这个字符串变量调用intern()方法后,意思就是将堆中的该字符串的副本放到字符串池中去。放之前先去看字符串池是否有该字符串,如果存在,就返回该字符串的引用,若不存在,就把该字符串添加一份到字符串池中。
JDK1.6后,
21、什么是拆箱与装箱?深入理解Integer类的拆箱与装箱?
首先,装箱与拆箱发生在基本数据类型和它们的包装类之间。Java具有自动拆箱装箱的功能,基本数据转包装器类型叫装箱,反之叫拆箱。如Integer i=5;(会触发自动装箱,等价于Integer i=new Integer(5)😉,int a=i;(会触发自动拆箱)。
通过Javap -c 类名,得到字节码发现,装箱时就是自动调用包装类的valueOf(int)方法,而拆箱就是自动调用包装类的xxxValue()方法(xxxValue()对应各自包装类)。
Integer i=127;Integer j=127;则ij为true,i、j指向同一对象。Integer i=250;Integer j=250;ij;为false,i、j指向不同对象。如果==其中一边出现表达式,就会触发自动拆箱,==就比较的是值而不是是否指向同一对象。
==:
1、int与int比较:数值相等就相等;
2、int与Integer比较,会触发自动拆箱,值相等就相等;
3、Integer与Integer比较,比较的是是否是同一对象,但-128~127包含两头,数值相等那就对象也就是同一个,所以相等;不再这个范围类,数值相等与否,都不是同一个对象。
4、若==左右存在表达式,会触发自动拆箱,比较的就是值。
equal()方法:对于基本数据类型,只要值相等就相等。对于引用类型,equal()方法不会进行类型转换,两边类型相等(如都为Integer)且值相等,那就相等,类型不相等就不相等。
22、String a="abc"与String b=new String(“abc”)的区别?及各创建了几个对象?
两者都先去字符串常量池中检查是否已经存在 “abc”,如果有就将字符串常量池的引用赋给两引用变量直接使用,如果没有则会在常量池中创建 “abc” 对象。
String b= new String(“abc”) 还会通过 new String() 在堆里创建一个内容与 “abc” 相同的对象实例。所以前者其实理解为被后者的所包含。
字符串常量池有“abc”:前者没创建对象;后置在堆中创建了一个,引用指向堆。
字符串常量池无“abc”:前者在字符串常量池中创建一个对象,并指向这个对象;后者创建两个对象,一个在堆中,一个在字符串常量池中,b引用指向的是堆中对象。
23、什么是面向对象?什么是类?它的三个特征是什么?
说面向对象前先说说面向过程,面向过程的思想就是去设计出解决问题的步骤,然后顺着这个步骤用函数一步一步实现。而面向对象就是将问题拆分成多个对象,每个对象有自己的属性和方法。通过调用对象的属性方法来解决问题。
总的来说,面向过程到面向对象是一种思想的转变,从我们怎样设计解决问题的步骤到我们解决这个问题需要用到什么,也就是一个一个的类,而面向对象程序设计重点也恰好就是去设计一个一个的类。
什么是类呢?类就是对一类事务的描述,是一种抽象数据结构,封装了属性和方法。Java中类和C语言的结构体和相似。
类三个特性就是1、封装:隐藏类的属性和实现细节,提供接口来供外部访问,这样可保护内部数据。2、继承:继承就是子类继承父类,这样子类就能使用父类的属性和方法了,实现代码的复用。3、多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态的前提是继承、父类引用指向子类对象、子类重写父类方法。同一个引用,通过改变指向不同的子类对象,调用的方法、属性也就不同。
25、类的初始化顺序?
父类静态代码块→子类静态代码块→父类非静态代码块→父类构造器→子类非静态代码块→子类构造器。静态代码块只执行一次。构造器是new几个对象执行几次。
27、手写冒泡排序
/**
*冒泡排序算法实现 :
*简单点就是双重for循环,一趟排序得到一个最值。
*/
public void bubble(int[] num)
{
/临时变量,交换值时使用/
int temp;
for(int i=0;i<num.length;i++)
{
for(int j=0;j<num.length-i-1;j++)
{
if(num[j] > num[j+1])
{
temp = num[j+1];
num[j+1] = num[j];
num[j] = temp;
}
}
}
}
2. 并发篇
什么是多线程 => 使用多线程的好处 => 如何使用多线程 => 多线程的并发安全问题 =>解决并发安全的方案:锁 => 锁的分类
1、什么是多线程?
2、使用多线程的好处?(为什么要使用多线程?)
3、如何使用多线程?
创建线程的几种方式:
使用线程池:
线程间的通信:
4、多线程的并发安全问题?
5、解决并发安全的方案:加锁 、不加锁?
参考:https://www.cnblogs.com/caiyiyang/p/14863326.html
5.1 加锁方案
5.1.1 有哪些锁?
公平锁、
非公平锁、
读写锁、
可重入锁、
轻量级锁、
重量级锁、
分布式锁、
行锁、
表锁、
死锁、
synchronized锁、
Lock锁、
自选锁、
排他锁、
共享锁、
乐观锁、
悲观锁
如何使用这些锁?
关于锁的问题及解决?
死锁问题
关于执行顺序的问题(8锁问题)
锁对性能影响的问题
锁的对比与各自适用的场景?
不加锁方案:
CAS思想:
利用局部变量:
ThreadLocal
不可变对象:如String类
如何使用锁
1、线程有几种状态
Java层面6种
在Thread.Static枚举类中定义了Java中线程的6种状态;新建、可运行、终结,中间穿插阻塞、等待、限时等待;
涉及方法:start()、wait()、sleep()、notify()、run()、getState()等;
操作系统层面5种
2、什么是进程?什么是线程?
单核CPU
多核CPU
进程间资源分配与通信
进程有操作系统来分配资源,多个进程共享同一系统资源,如CPU、内存、端口号等,进程间是相互独立的;
线程间资源分配与通信
线程存在于进程中,多个线程共用一个进程的资源,所以会出现各种线程安全问题;
使用多线程的好处:
3、线程池的7个参数
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
核心线程数:
最大线程数:
非核心线程存活时间:
时间单位:
任务队列:
线程工厂:
拒绝策略:4种,ThreadPoolExecutor中;
CallerRunsPolicy:让调用者自己处理该任务;
AbortPolicy:抛异常;
DiscardPolicy:满了就不管新的任务;
DiscardOldestPolicy:将任务队列中最早的任务丢弃,让新的任务进来;
4、说一说sleep()与wait()方法的区别 ?
每个对象都有一把属于自己的锁;
给对象加锁:可以用synchronized,也可以用Lock锁
获取对象的锁:synchronized会自动释放锁
wait()必须用在同步代码块中,也就是配合锁使用,如用在synchronized修饰代码块中、Lock中,sleep()则不需要;
wait会放弃锁,其它线程可以用该CPU资源,sleep不会释放锁,该对象的其它线程也不能得到CPU资源,必须等到sleep时间到了释放锁;
都可以被打断:即调用interrupt()方法来打断,抛出一个中断
异常;
5、Lock与synchronized的区别
6、阻塞队列
7、公平锁和非公平锁
8、条件变量Condition与signal()/signalAll()方法
9、volatile能否保证线程安全
线程安全分为三个方面:可见性、有序性、原子性;
可保证可见性、有序性,不能保证原子性!!;
可见性:不让JIT即时编译器介入优化代码,保证了可见性!!
有序性:使用内存屏障防止指令重排,保证了有序性!!
内存屏障:
原子性代码示例
可见性代码示例
有序性代码示例
10、Java中的悲观锁和乐观锁
理论
3. JVM篇