1. 八种基本数据类型的大小,以及他们的封装类
八种基本数据类型:int、short、float、double、long、boolean、byte、char。
涵盖 Java 基础数据类型、引用类型、自动装箱拆箱、静态与实例方法区别、构造方法、Object 常用方法、接口与抽象类、多态机制、对象创建方式、Switch 支持类型、集合框架(List/Set/Map)、反射机制、序列化、运算符区别、并发与并行等核心知识点,适合中级 Java 开发者面试复习。

八种基本数据类型:int、short、float、double、long、boolean、byte、char。
封装类分别是:Integer、Short、Float、Double、Long、Boolean、Byte、Character。

引用数据类型是由类的编辑器定义的,他们是用于访问对象的。这些变量被定义为不可更改的特定类型。
例如:Employee,Puppy 等等
自动装箱 在 jdk?1.5 之前,如果你想要定义一个 value 为 100 的 Integer 对象,则需要如下定义:
Integer i = new Integer(100);
int intNum1 = 100; //普通变量
Integer intNum2 = intNum1; //自动装箱
int intNum3 = intNum2; //自动拆箱
Integer intNum4 = 100; //自动装箱
上面的代码中,intNum2 为一个 Integer 类型的实例,intNum1 为 Java 中的基础数据类型,将 intNum1 赋值给 intNum2 便是自动装箱;而将 intNum2 赋值给 intNum3 则是自动拆箱。
八种基本数据类型:boolean byte char shrot int long float double ,所生成的变量相当于常量。
基本类型包装类:Boolean Byte Character Short Integer Long Float Double。
自动拆箱和自动装箱定义:
自动装箱是将一个 java 定义的基本数据类型赋值给相应封装类的变量。拆箱与装箱是相反的操作,自动拆箱则是将一个封装类的变量赋值给相应基本数据类型的变量。
由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。
Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中'没有参数的构造方法'。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。
但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。
所以,实际上 java 和 javax 没有区别。这都是一个名字。
Object 是所有类的父类,任何类都默认继承 Object
clone 保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。
equals 在 Object 中与==是一样的,子类一般需要重写该方法。
hashCode 该方法用于哈希查找,重写了 equals 方法一般都要重写 hashCode 方法。这个方法在一些具有哈希功能的 Collection 中用到。
getClass final 方法,获得运行时类型
wait 使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生
1、其他线程调用了该对象的 notify 方法。2、其他线程调用了该对象的 notifyAll 方法。3、其他线程调用了 interrupt 中断该线程。4、时间间隔到了。5、此时该线程就可以被调度了,如果是被中断的话就抛出一个 InterruptedException 异常。
notify 唤醒在该对象上等待的某个线程。
notifyAll 唤醒在该对象上等待的所有线程。
toString 转换成字符串,一般子类都有重写,否则打印句柄。
接口的意义用三个词就可以概括:规范,扩展,回调。
抽象类的意义可以用三句话来概括:
| 比较点 | 抽象类 | 接口 |
|---|---|---|
| 默认方法 | 抽象类可以有默认的方法实现 | java 8 之前,接口中不存在方法的实现 |
| 实现方式 | 子类使用 extends 关键字来继承抽象类。如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现 | 子类使用 implements 来实现接口,需要提供接口中所有声明的实现。 |
| 构造器 | 抽象类中可以有构造器 | 接口中不能 |
| 和正常类区别 | 抽象类不能被实例化 | 接口则是完全不同的类型 |
| 访问修饰符 | 抽象方法可以有 public,protected 和 default 等修饰 | 接口默认是 public,不能使用其他修饰符 |
| 多继承 | 一个子类只能存在一个父类 | 一个子类可以存在多个接口 |
| 添加新方法 | 抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码 | 如果往接口中添加新方法,则子类中需要实现该方法 |
不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏。
不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象,如 String、Integer 及其它包装类。不可变对象最大的好处是线程安全。
静态变量存储在方法区,属于类所有。实例变量存储在堆当中,其引用存在当前线程栈。需要注意的是从 JDK1.8 开始用于实现方法区的 PermSpace 被 MetaSpace 取代了。
能否创建一个包含可变对象的不可变对象?
当然可以,比如 final Person[] persons = new Persion[]{}。persons 是不可变对象的引用,但其数组中的 Person 实例却是可变的。这种情况下需要特别谨慎,不要共享可变对象的引用。这种情况下,如果数据需要变化时,就返回原对象的一个拷贝。
new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)
方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!
主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
静态方法和实例方法的区别主要体现在两个方面:
对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。
帮助子类做初始化工作。
多态,就是重载和重写。重载发生在一个类中。重写发生在子类,意思就是子类重写父类相同名称的方法。刚学语言有的东西,不必搞得那么清楚,只有知道怎么用就行了,有的问题你要想真正把它搞得很懂,短时间是不可能的,比如说接口,没有几年工作经验你根本不可能真正理解什么是接口,甚至有的人工作四,五年也没搞明白什么是接口,不要花太多时间去搞这些不容易搞懂的问题。
重载的特性,方法名相同。返回类型,传入方法的参数不同 (包括个数和类型)。 重写的特性,方法名相同,返回类型,参数均相同,必须发生在子类。
1.Java 语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换。 Animal animal=new Dog(); //引用变量 animal 引用一个 Animal 子类 Dog 的实例 Dog dog=(Dog)animal; //向下转型,把 Animal 类型转换为 Dog 类型 Creature creature=animal; //向上转型,把 Animal 类型转换为 Creature 类型 animal=new Cat(); //引用变量 animal 引用另一个 Animal 子类 Cat 的实例 注:Creature 这里指的是生物。
2.如果把引用变量转换为子类类型,称为向下转型,如果把引用变量转换为父类类型,称为向上转型。
3.在进行引用变量的类型转换时,会受到各种限制。而且在通过引用变量访问它所引用的实例的静态属性、静态方法、实例属性、实例方法,以及从父类中继承的方法和属性时,Java 虚拟机会采用不同的绑定机制。
4.成员变量、静态方法按照引用变量声明的类型静态绑定;实例方法按照引用变量引用的实例动态绑定。 例如,对于以下这段代码:
Fathers f = new Sons();
java 中提供了以下四种创建对象的方式:
前两者都需要显式地调用构造方法。对于 clone 机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在 java 中序列化可以通过实现 Externalizable 或者 Serializable 来实现。
在 JDK 1.7 之前,switch 只能支持 byte,short,char,int 或者其对应的包装类以及 Enum 类型。从 JDK 1.7 之后 switch 开始支持 String 类型。但到目前为止,switch 都不支持 long 类型。
equals(),clone(),getClass(),notify(),notifyAll(),wait(),toString
== 和 eqauls() 的区别?== 是运算符,用于比较两个变量是否相等,对于基本类型而言比较的是变量的值,对于对象类型而言比较的是对象的地址.equals() 是 Object 类的方法,用于比较两个对象内容是否相等。默认 Object 类的 equals() 实现如下:public class Object {
// ... implementation
}
不难看出此时 equals() 是比较两个对象的地址,此时直接 == 比较的结果一样。对于可能用于集合存储中的对象元素而言,通常需要重写其 equals() 方法.
如果 a 和 b 都是对象,则 a==b 是比较两个对象内存地址,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true。而 a.equals(b) 是进行内容比较,其比较结果取决于 equals() 具体实现。多数情况下,我们需要重写该方法,如 String 类重写 equals() 用于两个不同对象,但是包含的字母相同的比较:
public boolean equals(Object anObject) {
// ... implementation
}
equals() 和 hashcode() 的联系hashCode() 是 Object 类的一个方法,返回一个哈希值。如果两个对象根据 equal() 方法比较相等,那么调用这两个对象中任意一个对象的 hashCode() 方法必须产生相同的哈希值;如果两个对象根据 eqaul() 方法比较不相等,那么产生的哈希值不一定相等 (碰撞的情况下还是会相等的.)
hashCode() 方法是为对象产生整型的 hash 值,用作对象的唯一标识。它常用于基于 hash 的集合类,如 Hashtable,HashMap 等等。根据 Java 规范,使用 equal() 方法来判断两个相等的对象,必须具有相同的 hashcode.
将对象放入到集合中时,首先判断要放入对象的 hashcode 是否已经在集合中存在,不存在则直接放入集合。如果 hashcode 相等,然后通过 equal() 方法判断要放入对象与集合中的任意对象是否相等:如果 equal() 判断不相等,直接将该元素放入集合中,否则不放入。
有可能。在产生 hash 冲突时,两个不相等的对象就会有相同的 hashcode 值。当 hash 冲突产生时,一般有以下几种方式来处理:
不行,因为同一对象的 hashcode 值必须是相同的。
Map
Set
List
Queue
Stack
用法
更为精炼的总结
Collection 是对象集合,Collection 有两个子接口 List 和 Set
List 可以通过下标 (1,2..) 来取得值,值可以重复。Set 只能通过游标来取值,并且值是不能重复的。
ArrayList,Vector,LinkedList 是 List 的实现类
Map 是键值对集合
Stack 类:继承自 Vector,实现一个后进先出的栈。提供了几个基本方法,push、pop、peak、empty、search 等。
Queue 接口:提供了几个基本方法,offer、poll、peek 等。已知实现类有 LinkedList、PriorityQueue 等。

List 转换成为数组:调用 ArrayList 的 toArray 方法。 数组转换成为 List:调用 Arrays 的 asList 方法。
Array 可以容纳基本类型和对象,而 ArrayList 只能容纳对象。 Array 是指定大小的,而 ArrayList 大小是固定的。 Array 没有提供 ArrayList 那么多功能,比如 addAll、removeAll 和 iterator 等。
1、ArrayList 是基于索引的数据接口,它的底层是数组。它可以以 O(1) 时间复杂度对元素进行随机访问。与此对应,LinkedList 是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是 O(n)。 2、相对于 ArrayList,LinkedList 的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。 3、LinkedList 比 ArrayList 更占内存,因为 LinkedList 为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素
TreeMap<K,V>的 Key 值是要求实现 java.lang.Comparable,所以迭代的时候 TreeMap 默认是按照 Key 值升序排序的;TreeMap 的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap<K,V>的 Key 值实现散列 hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶 (数组),链表或红黑树。适用于在 Map 中插入、删除和定位元素。
如果你需要得到一个有序的结果时就应该使用 TreeMap(因为 HashMap 中元素的排列顺序是不固定的)。除此之外,由于 HashMap 有更好的性能,所以大多不需要排序的时候我们会使用 HashMap。
55. 什么是 Java 的反射呢?
Java 反射是可以让我们在运行时,通过一个类的 Class 对象来获取它获取类的方法、属性、父类、接口等类的内部信息的机制。
这种动态获取信息以及动态调用对象的方法的功能称为 JAVA 的反射。
56. 反射的作用?
反射就是:在任意一个方法里:
如果我知道一个类的名称/或者它的一个实例对象,我就能把这个类的所有方法和变量的信息找出来 (方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)
如果我还明确知道这个类里某个变量的名称,我还能得到这个变量当前的值。
当然,如果我明确知道这个类里的某个方法名 + 参数个数类型,我还能通过传递参数来运行那个类里的那个方法。
57. 反射机制主要提供了以下功能:
JAVA 语言编译之后会生成一个.class 文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。
反射的实现主要借助以下四个类:
Class:类的对象Constructor:类的构造方法Field:类中的属性对象Method:类中的方法对象序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。 注意事项:
基础的概念不能弄混:&是位操作,&&是逻辑运算符。需要记住逻辑运算符具有短路特性,而&不具备短路特性。来看看一下代码执行结果?
public class Test{
static String name;
public static void main(String[] args){
if(name!=null&userName.equals("")){
System.out.println("ok");
}else{
System.out.println("erro");
}
}
}
上述代码将会抛出空指针异常。原因你懂得。
在一个 java 文件中只能有一个 public 公共类,但是可以有多个 default 修饰的类。
内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类。创建内部类对象的时刻不依赖于外部类对象的创建。内部类并没有令人疑惑的'is-a'关系,它就像是一个独立的实体。此外,内部类提供了更好的封装,除了该外围类,其他类都不能访问。
三者没有任何相关性,遇到有问着问题的面试官就拖出去砍了吧。
final 是一个修饰符,用于修饰变量,方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变。
finalize() 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会。但是该方法由 Finalizer 线程调用,但调用时机无法保证。
finally 是一个关键字,与 try 和 catch 一起用于异常的处理,finally{}一定会被执行,在此处我们通常用于资源关闭操作。
类、变量、方法 final 修饰的类叫最终类,该类不能被继承。 final 修饰的方法不能被重写。 final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
hashCode 的存在主要是用于查找的快捷性,如 Hashtable,HashMap 等,hashCode 是用来在散列存储结构中确定对象的存储地址的。
如果两个对象相同,就是适用于 equals(java.lang.Object) 方法,那么这两个对象的 hashCode 一定要相同。
如果对象的equals 方法被重写,那么对象的 hashCode 也尽量重写,并且产生 hashCode 使用的对象,一定要和 equals 方法中使用的一致,否则就会违反上面提到的第 2 点。
两个对象的 hashCode 相同,并不一定表示两个对象就相同,也就是不一定适用于 equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如 Hashtable,他们'存放在同一个篮子里'。
什么时候需要重写?
一般的地方不需要重载 hashCode,只有当类需要放在 HashTable、HashMap、HashSet 等等 hash 结构的集合时才会重载 hashCode,那么为什么要重载 hashCode 呢?
要比较两个类的内容属性值,是否相同时候,根据 hashCode 重写规则,重写类的 指定字段的 hashCode(),equals() 方法。
例如
public class EmpWorkCondition{
// ...
}
输出结果:true
上面的方法,做的事情就是,比较两个集合中的,实体类对象属性值,是否一致
OrderSum 不在比较范围内,因为没有重写它的,equals() 和 hashCode() 方法
为什么要重载 equal 方法?
因为 Object 的 equal 方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载 equal 方法。
HashMap 在并发执行 put 操作时会引起死循环,导致 CPU 利用率接近 100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。
// 1、利用 key 的 hashCode 重新 hash 计算出当前对象的元素在数组中的下标
我们很容易想到用 final 关键字进行修饰,我们都知道
final 关键字可以修饰类,方法,成员变量,final 修饰的类不能被继承,final 修饰的方法不能被重写,final 修饰的成员变量必须初始化值,如果这个成员变量是基本数据类型,表示这个变量的值是不可改变的,如果说这个成员变量是引用类型,则表示这个引用的地址值是不能改变的,但是这个引用所指向的对象里面的内容还是可以改变的。
那么,我们怎么确保一个集合不能被修改?
首先我们要清楚,集合(map,set,list…)都是引用类型,所以我们如果用 final 修饰的话,集合里面的内容还是可以修改的。
我们可以采用 Collections 包下的 unmodifiableMap 方法,通过这个方法返回的 map,是不可以修改的。他会报 java.lang.UnsupportedOperationException 错。
同理:Collections 包也提供了对 list 和 set 集合的方法。
Collections.unmodifiableList(List) Collections.unmodifiableSet(Set)
**并行:**在操作系统中是指,一组程序按独立异步的速度执行,不等于时间上的重叠(同一个时刻发生)。 **并发:**在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。
并发和并行的作用
通过并发和并行能够使得应用程序可以充分利用多核以及 GPU 的计算能力,从而提高应用程序的性能,比如在以下几个方面中:
使用异步 I/O 操作可以提高应用程序的响应性。大多数的 GUI 应用程序都是用单个线程来控制所有 UI 界面的更新。UI 线程不应该被占用过长时间,不然 UI 界面就会失去对用户的响应。
跨多线程的并行工作可以更好的利用系统的资源。具有多 CPU 和 GPU 的现代计算机,通过并行可以指数级的提高 CPU 计算受限的应用程序的性能。
同时执行多个 I/O 操作(如同时从多个网站上获取信息)可以提高总体的吞吐量(throughput),等待 I/O 相应的操作可以用来发起新的操作,或者是处理操作返回的结果。
并行和并发区别
区别一: 并发是指一个处理器同时处理多个任务。并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
区别二: 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的。
区别三: 当有多个线程在操作时,如果系统只有一个 CPU,则它根本不可能真正同时进行一个以上的线程,它只能把 CPU 运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态。这种方式我们称之为并发(Concurrent)。
当系统有一个以上 CPU 时,则线程的操作有可能非并发。当一个 CPU 执行一个线程时,另一个 CPU 可以执行另一个线程,两个线程互不抢占 CPU 资源,可以同时进行,这种方式我们称之为并行(Parallel)。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online