- 基础
- 重点
- 1、continue break return
- 2、泛型擦除
- 3、==和equals
- 4、方法参数传递
- 5、重载和重写
- 6、拷贝
- 7、面向对象
- 8、static、super、this
- 9、变量
- 10、继承
- 11、接口和抽象类区别
- 12、String、StringBuffer、StringBuilder
- 13、transient
- 14、ArrayList和Vector区别
- 15、ArrayList和LinkedList区别
- 16、HashSet、LinkedHashSet、TreeSet
- 17、Set是无序的,为什么说TreeSet是有序的
- 18、HashTable和HashMap区别
- 19、HashMap遍历方式
- 20、创建线程的四种方式
- 21、sleep和wait区别
- 22、Java对象的创建过程
- 23、单例模式怎么实现
- 24、单例模式懒汉式为什么线程不安全
- 25、双检锁为什么效率更高
- 26、JVM内存结构:
- 27、字节码文件转换
基础
java区分大小写
1.标识符
定义:程序中自定义的一些名称,由数字、字母、_、$组成
规则:不可以数字开头,不可以使用关键字
规范:
- 自定义时尽量取有意义的名称
- 包名:所有字母小写
- 类名和接口名:单词首字母大写
- 变量名和方法名:第二个单词开始首字母大写
- 常量名:所有字母大写
分类 | 关键字 |
---|---|
访问控制 | private default protected public |
修饰符 | abstract class extends final implements interface native new static strictfp synchronized transient volatile |
程序控制 | break continue return do while if else for instanceof switch case default |
错误处理 | try catch throw throws finally |
包相关 | import package |
基本类型 | boolean byte char double float int long short null true false |
变量引用 | super this void |
保留字 | goto const |
2.常量和变量
常量:数值不能改变
- 定义时需要进行初始化
- 使用final关键字定义
变量:
- 意义:可以理解为内存中某个存放数据的空间的名字
- 三元素:变量类型、变量名、变量值
- 小驼峰命名法:第二个单词开始首字母大写
- 数值可以改变
- 成员变量(实例变量)有默认值,局部变量先初始化后使用,静态变量(类变量)属于类。
3.数据类型
基本数据类型:
1.数值型
整数类型:
byte(1字节)、short(2字节)
int( 4字节)、long(8字节)
浮点类型:
float(4字节)、double(8字节)
2.字符型:char(2字节)
3.布尔型:boolean(1字节)
引用数据类型:
类、接口、数组、枚举、注解
注意:基本类型内存中只有一块对应的内存空间;引用类型有两块,一块存内容,一块存地址。
4.运算符
算术运算:+、-、*、/、%、++、- -
- 整数相除为整数(直接舍去小数位)
- 小数计算结果不精确(因为计算机是二进制格式存储小数的,所以只能精确表示2的某次方之和的数)
- a=1;b=a++;结果:a=2;b=1;
- a=1;b=++a;结果:a=2;b=2;
比较运算:>、<、>=、<=、==、!=
- =属于赋值,==判断地址是否相同
逻辑运算:
- &:一者为false则false
- |:一者为true则true
- !:true则false,false则true
- ^:两者同则false,不同则true
- &&:前者false则不判断后者
- ||:前者true则不判断后者
5.流程控制语句
- if语句
- if/else语句
- switch语句
- for语句
- for each语句
- do/while语句
- while语句
注意
- if后面不加{}则只执行第一条语句,if(…);(空语句)
- break用于提前结束;A:for()…break A;等同于goto
- continue用于跳过循环体剩余代码,执行步进
- if/else可用三元运算符简写 变量=(条件表达式)?表达式1:表达式2
6.方法
- 修饰符 返回值类型 方法名(参数列表){return 返回值};
- 重载:同一个类中,允许不同方法的方法名相同,只要它们的参数列表不同即可
- 重写:子类重写父类的方法(private不行),且方法权限修饰符不能低于父类,返回值类型、方法名、参数列表都一样
- 构造方法是在创建实例时被调用的;
- 未定义构造方法的类中,会自动定义一个不接收参数,主体为空的默认构造方法
- 使用 super(实参列表) 调用超类的构造方法
- 使用 this(实参列表)调用同一类中其他构造方法
7.数组
一维数组声明:
- 数组类型[] 数组名称;
- 数组类型 数组名称[];
- 数组类型[] 数组名称=new 数组类型[]{ , , , };
二维数组声明:例如
(1)int[] [] array=new int[5] [3];
(2)int[] [] array=new int[2][];
array[0]=new int[3];
array[1]=new int[4];
可以使用for each遍历数组
8.注释
- 单行注释//
- 多行注释/* */
- 文档注释/** */
重点
1、continue break return
continue:指跳出当前的这一次循环,继续下一次循环。
break:指跳出整个循环体,继续执行循环下面的语句。
return:用于跳出所在方法,结束该方法的运行。
2、泛型擦除
编译器检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1==c2);
====输出true====
编译器在编译时会将ArrayList<E>
转换为ArrayList
将ArrayList类中的E都转换为指定的类型
比如ArrayList<String>
的add(E e)会转换为add(String e)
3、==和equals
基本数据类型==比较的是值; 引用数据类型==比较的是内存地址
equals没有覆盖等价于== ; 覆盖比较的是内容
Byte,Short,Integer,Long默认创建了数值[-128,127] 的缓存数据
Character默认创建了数值在[0,127]范围的缓存数据
Integer a = 110 ;
Java 在编译的时候会直接将代码封装成 Integer a = Integer.valueOf(110);
由于值在范围内,从而使用常量池中的对象。
Integer a = 120 ;
Java 在编译的时候会直接将代码封装成 Integer a = Integer.valueOf(110);
由于值不在范围内,从而创建新的对象。
Integer i1 = new Integer(110);
这种情况下会创建新的对象。
4、方法参数传递
基本数据类型 传递 值,引用类型 传递 地址
5、重载和重写
重载:重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理;代码体现在方法名相同,参数列表不同
重写:重写是当子类继承父类的方法不满足需求时,需要对继承的父类方法进行覆盖;返问权限不能低于父类,返回类型和异常不能高于父类
6、拷贝
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
7、面向对象
面向对象是以对象作为基本单位的语言
面向对象有四大特征:抽象、封装、继承、多态;
七大原则:
- 单一职责原则:一个类应该只负责一项职责
- 接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
- 依赖倒转原则:面向接口编程,不要面向实现编程
- 里氏替换原则:在使用父类的地方,可以使用子类替换
- 开闭原则:当软件需要变化时,通过扩展软件实体的行为来实现变化,而不是通过修改代码来实现变化
- 迪米特法则:一个对象应该对其他对象保持最少的了解
- 合成复用原则:尽量使用合成复用将已有对象纳入新对象,而不是使用继承复用
相对于面向过程,面向对象的性能会低一些,但维护性和扩展性高
8、static、super、this
static方法或代码块中不能使用this/super:
- 因为static方法/代码块在对象创建之前执行
super:在构造方法第一行隐式调用
在构造器中使用 super() 调用父类中的其他构造方法时或this()调用本类中的其他构造方法时,该语句必须处于构造器的首行
super()和this()不能同时出现在一个构造方法中
9、变量
成员变量和局部变量区别:
- 作用域不同,局部变量是作用在局部,比如代码块、方法;成员变量是全局有效
- 成员变量有默认值,局部变量没有默认值,在使用前需先赋值
成员变量中类变量和实例变量的区别:
- 类变量是实例共享,实例变量是实例私有
10、继承
子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
11、接口和抽象类区别
从设计层面看,接口是对行为的抽象,抽象类是对对象的抽象
抽象类可以有非抽象的方法,而接口是从jdk8开始可以实现静态、默认方法,jdk9实现私有方法
类可以实现多个接口,但只能继承一个抽象类
抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)
12、String、StringBuffer、StringBuilder
String不可变
StringBuffer可变,线程安全、性能低
StringBuilder可变,线程不安全、性能高
13、transient
阻止实例中那些用此关键字修饰的的变量序列化;
当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复
transient 只能修饰变量,不能修饰类和方法。
14、ArrayList和Vector区别
ArrayList线程不安全 但性能高
Vector线程安全 但性能低。
开发中常用CopyOnWriteArrayList,比如JDBC中从驱动管理器中获取连接,驱动管理器底层就是用CopyOnWriteArrayList实现
CopyOnWriteArrayList读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,因此可以保证数据安全。
写入操作 add() 方法在添加集合的时候加了锁,保证同步,避免多线程写的时候会 copy 出多个副本。
15、ArrayList和LinkedList区别
ArrayList基于数组实现,查找修改效率高
LinkedList基于双向链表实现,增加删除效率高
16、HashSet、LinkedHashSet、TreeSet
HashSet 的底层是 HashMap
LinkedHashSet 是 HashSet 的子类
TreeSet 底层使用红黑树
17、Set是无序的,为什么说TreeSet是有序的
无序是指输入顺序和输出顺序不一样,Set因为存储数据时根据数据的hashcode来排位置,所以输入输出顺序不一样,
TreeSet因为存储数据会按数据的值大小进行一次排序(有序),所以输入输出顺序不一样
18、HashTable和HashMap区别
HashTable线程安全、性能低;HashMap线程不安全、性能高
在hash冲突达到8时,HashMap会将链表转换为红黑树,提高性能
ConCurrentHashMap:底层数据结构是数组+链表+红黑树,jdk8前使用分段锁,jdk8开始使用乐观锁CAS+悲观锁Synchronize
19、HashMap遍历方式
迭代器遍历、for each遍历:1.直接取键值 2.先取键后取值
20、创建线程的四种方式
继承Thread类、实现Runnable接口、实现Callable、通过线程池创建
21、sleep和wait区别
wait会释放锁、sleep不会释放锁
22、Java对象的创建过程
1.类加载检查、分配内存、初始化零值、设置对象头、执行init方法
23、单例模式怎么实现
将构造器和静态对象私有化,提供一个公共的静态方法来给外界访问对象。
24、单例模式懒汉式为什么线程不安全
在多线程时,当一个线程通过判断后,在未创建对象前,另外一个线程也在进行判断,由于对象未创建,另外一个线程也会通过判断
25、双检锁为什么效率更高
在锁外面再加一个判断,则对象创建后,剩下的线程将验证失败,所以可以并行处理,不必一个一个验证。
26、JVM内存结构:
类加载器子系统:
加载:采用双亲委派机制加载、沙箱安全机制,
如果class文件的类加载器是系统类加载器,则判断系统类加载器是否加载过class文件
如果没有加载,则向上判断扩展类加载器是否加载过
如果也没有,则向上判断引导类加载器是否加载过
如果没有,则使用引导类加载器加载,
如果不能加载则向下找引导类加载器加载,
如果不能加载则向下找系统类加载器加载
引导类加载器加载核心类库比如java.lang包下的类库
扩展类加载器加载java运行环境下的jar包中的扩展类库
系统类加载器加载环境变量下的类库
链接:
验证阶段:文件格式验证、元数据验证、符号引用验证、字节码验证
准备阶段:为类变量分配内存并赋默认值
解析阶段:将常量池中的符号引用转为直接引用的过程
初始化:执行<clinit>方法,<clinit>方法由静态变量显示赋值代码和静态代码块构成
运行时数据区:也叫Java内存结构
本地方法栈:管理本地方法的调用,在本地方法栈中登记本地方法,在执行引擎执行时加载本地方法库
Java虚拟机栈:由栈帧组成,栈帧由局部变量、操作数栈、方法返回地址、动态链接(通过运行时常量池的符号引用实现)构成
程序计数器:控制代码流程和保存当前线程执行的位置
堆:分为年轻代和老年代,年轻代分为Eden区和幸存者From区To区
新建的对象放在伊甸园区,伊甸园区如果满了先会执行GC,将伊甸园区中不再被其他对象引用的对象进行回收,
GC后的剩余对象放入幸存者From区并设年龄计算器为1
方法区:类型信息(类、接口、枚举、注解)、域信息(属性)、方法信息、即时编译器的代码缓存、运行时常量池
JDK7开始将方法区的静态变量和运行常量池中的字符串常量池放入了堆区
执行引擎:
本地方法接口:
本地方法库:
27、字节码文件转换
JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。
而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器
而 JIT 属于运行时编译,当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。
而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。