最近在复习一下Java的一些基础东西,我整理了一部分,有错误的地方大家可以留言,部分内容我也是从网上学习(抄袭)来的,后续还会复习整理。
1.Java语言得特点:
1.简单易学,上手容易 2.可跨平台(在自己得JVM上运行) 3.安全性高 4.面向对象思想 5.支持多线程
2.java的八大基本数据类型:
byte(1),short(2),int(4),long(8),float(4),double(8),boolean,char(2)
3.instanceof 的含义和用法:
用于检测对象是否是指定class 的子类,类实例,或接口实现类
4.java 的自动装箱和拆箱
装箱:将基本数据类型转换成包装类型(引用类型) 例:int -> Integer( Integer.valueof(int) ) Integer i = 10;(自动装箱,自动将int 包装成 Integer)
拆箱:将包装类型转换成对应的基本数据类型 :例:Integer i = 1; int in = i.intValue();
注意:(创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。)
注意:在某个范围内的整型数值的个数是有限的,而浮点数有无穷个。
5.重写与重载的区别:
重写:发生的子父类之间
方法名称相同,参数相同,返回类型相同
不能抛出比父类方法更多的异常
重写方法 的访问修饰符必须高于 被重写的方法。
重载:发生在同一个类中
方法名称相同,参数的个数,类型,顺序不同,返回类型可以不同
6.equals 和 == 的区别
== 是比较的栈中的对象地址是否相同,比较是否指向同一对象(堆),比较的实际意义是指针的比较。
equals是Object的一个方法,返回的是== 的结果。其他对象在不重写equals的情况下,equals 与 ==相同
7.String是一个只读字符串,并不是基本类型,StringBuffer和StringBuilder底层都是一个可操作的字符数组,StringBuffer是线程安全的,StringBuilder线程不安全
8.arrayList 和 linkedList的区别:
arrayList 底层是数组结构,linkedList底层是双向链表结构。
他们都是一个有序列表,保存的值可以相同
基于他们底层数据结构的问题,linkedList 删除和添加更快(如果是尾部添加 它们没啥差别),arrayList查询更快,arrayList可以指定下标查询。
linkedList对于内存的连续性要求低,arrayList相对高一些,因为arrayList底层是一个数组。
9.hashMap 和 hashTable 的区别
hashMap 的父类是AbstractMap,hashTable的父类是Dictionary
hashMap允许value为NULL,key只能有一个为NULL,hashaTable key 和 value 都不能为NULL
hashMap是线程不安全的,hashTable线程安全的
如果需要线程安全建议使用ConcurrentHashMap 程 因为他是分段所,知识锁定key hash后的那个桶。
hashTable 默认初始容量11 hashMap的初始值 16 (hashMap的初始值设定时会根据用户设定的参数计算一个正确的初始值,例如我们在初始化时设定 new HashMap(15) hashmap经过计算,这时候我们的hashMap的最终初始值 是16)
hashTable 与 hashMap 的hash方法不相同
10.collection 和 collections 的区别
collection 是 集合的上级接口(list,set 都是继承了collection)
collections是集合的一个帮助类,主要提供一些排序,搜索,线程安全化(List<String> s = Collections.synchronizedList(new ArrayList<String>());)
11.java的四种引用,强弱软虚
强引用:我们平常用的最多的,强引用在gc时不会被回收。( String s = new String("s") )
弱引用:弱引用就是只要gc发现了它,就会被回收。(WeakReference<String> wrf = new WeakReference<String>(str);)如果需要使用可以参考(java.util.WeakHashMap)
另外两个不讲了都与弱引用差不太多。
12.Java创建对象的几种方式:
New创建新对象:
通过反射创建对象:(反射创建对象的方法是调用class 的 newInstance() 方法,其本质是调用Constructor<T>的newInstance()方法,
所以我们经常这么用:Constructor constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();
关于获取对象的Class 有一下几种方法
//创建Class对象的方式一:(对象.getClass()),获取person类中的字节码文件
employee.class
//创建Class对象的方式二:(类.class:需要输入一个明确的类,任意一个类型都有一个静态的class属性)
Employee.class
//创建Class对象的方式三:(forName():传入时只需要以字符串的方式传入即可)
//通过Class类的一个forName(String className)静态方法返回一个Class对象,className必须是全路径名称;
//Class.forName()有异常:ClassNotFoundException
Class.forName("cn.itcast.Person");)
通过Clone得方式创建对象:(需要注意:clone方法需要类实现Cloneable并重写clone方法,重写克隆方法后,类
得无参构造必选显式声明。拷贝也分深拷贝和浅拷贝,不了解得话可以百度。)
通过序列化的方式创建对象:(ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Cat emp5 = (Cat) in.readObject();
in.close();
emp5.setName("Akash");
data.obj 是ObjectOutputStream 输出得一个对象序列化文件)
13.不同的对象会不会存在相同的hashcode
存在的,因为hash冲突时不可避免的存在的。
当我们遇到这种情况时怎么处理那:
a:hashMap的处理方式:增加一个链表,在链表上放hashcode相同的对象
b:理论上来说可以在换一个hash算法
c:如果冲突了,随便找一个空地址先放着再说。
14.final 的相关特性和用法
被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
被final修饰的方法,JVM会尝试将其内联,以提高运行效率
被final修饰的常量,在编译阶段会存入常量池中
(除此之外,编译器对fifinal域要遵守的两个重排序规则更好:
在构造函数内对一个fifinal域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之
间不能重排序
初次读一个包含fifinal域的对象的引用,与随后初次读这个fifinal域,这两个操作之间不能重排序.)
15.关于static的用法
static 常用的静态方法,静态类,静态内部类,静态代码块,除了这些外还有一个不太常用的就是静态导包( import static .import) 可以用来导入静态资源,在使用时可以不写类名
import static java.lang.Math.*;
public class Test{
public static void main(String[] args){
//System.out.println(Math.sin(20));传统做法
System.out.println(sin(20));
}
}
16.关于浮点运算精度丢失问题:
例如: 3*0.1 其结果就不等于0.3
6*0.1 其结果也不能与0.6
17.a=a+b与a+=b有什么区别吗?
+=会隐式的将右侧的数据强转成左侧的数据类型
byte a = 127; byte b = 127;
//b = a + b; // 报编译错误:cannot convert from int to byte
b += a;
System.err.println(b);
这里输出的是 -2 (我的计算方法:b = (127+1)->-128 +(127-1)->126 所以等于-2)
18.try catch finally,try里有return,finally还执行么?
执行;早于try中return执行;
public static String dd() {
String name = "x";
try {
return name = name+"j";
} finally {
return name+"w";
}
}
看上边这个方法,它的返回值是多少,答案是xjw。 所以我们的结论是
当 finally 和 try 里都有return时,先执行try中return 后的表达式但不反回结果,接着执行finally中的return并直接返回这个结果。
所以当我们写代码时finally中最好不要有return,否则结果可能不是你预期的。
19.Exception 和 error
在java中异常分为两种,运行时异常和非运行时异常(编码时编辑器就会报错,或者编译时直接报错)
运行时异常(RuntimeException)
ClassCastException(类转换异常)
NullPointerException(空指针异常)
IndexOutOfBoundsException(数组越界异常)
。。。。。 这些都是运行时异常。
非运行时异常是在我们编码需要显示的捕获或者抛出的异常。
error 程序错误(典型的内存溢出错误),它和运行时异常异常只有当发生时我们才知道。通常出现这种错误时程序会终止运行。
20. OOM你遇到过哪些情况,SOF你遇到过哪些情况
OOM:
1,OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可
能。
Java Heap 溢出:
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess。
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免
垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转
存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是
内存溢出(Memory Overflflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GCRoots的引用链。于是就能找到泄漏对象是通过
怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。
2,虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflflowError异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
这里需要注意当栈的大小越大可分配的线程数就越少。
3,运行时常量池溢出
异常信息:java.lang.OutOfMemoryError:PermGenspace
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的
作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否
则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在
方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量
池的容量。
4,方法区溢出
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。也有可能
是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。
异常信息:java.lang.OutOfMemoryError:PermGenspace
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在
经常动态生成大量Class的应用中,要特别注意这点。
SOF(堆栈溢出StackOverflflow):
StackOverflflowError 的定义:当应用程序递归太深而发生堆栈溢出时,抛出该错误。
因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量
超过1m而导致溢出。
栈溢出的原因:递归调用,大量循环或死循环,全局变量是否过多,数组、List、map数据过大。
21.线程的基本情况:
下边是线程在运行过程的状态,共6种:
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3.阻塞(BLOCKED):表示线程阻塞于锁。
4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。
线程的状态图
当线程执行 wait() 方法之后,线程进入 WAITING(等待)状态。进入等待状态的线程需要依靠其他
线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加
了超时限制,比如通过 sleep(long millis) 方法或 wait(long millis) 方法可以将 Java 线程置
于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步
方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable
的 run() 方法之后将会进入到 TERMINATED(终止) 状态。
22.在java中如果对象不想被序列化应该怎么做
transient 这个关键字了解一下, 在实现了Serializable的类中在不想被序列化的字段前用transient修饰 ,它只能修是变量,不能修饰方法和类。
23、Java 中 IO 流
Java 中 IO 流分为几种?
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的
联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作方式分类结构图:
详细了解可以参考: https://www.jianshu.com/p/715659e4775f
24、 Java IO与 NIO的区别
25、java反射的作用于原理
1、定义:
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能
够调用它的任意一个方法。在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
2、哪里会用到反射机制?
jdbc就是典型的反射
这就是反射。如hibernate,struts等框架使用反射实现的。
3、反射的实现方式:
第一步:获取Class对象,有4中方法:
1)Class.forName(“类的路径”);
2)类名.class
3)对象名.getClass()
4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
4、实现Java反射的类:
1)Class:表示正在运行的Java应用程序中的类和接口
注意: 所有获取对象的信息都需要Class类来实现。
2)Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
3)Constructor:提供关于类的单个构造方法的信息以及它的访问权限
4)Method:提供类或接口中某个方法的信息
5、反射机制的优缺点:
优点:
1)能够运行时动态获取类的实例,提高灵活性;
2)与动态编译结合
缺点:
1)使用反射性能较低,需要解析字节码,将内存中的对象进行解析。
解决方案:
1、通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
2、多次创建一个类的实例时,有缓存会快很多
3、ReflflectASM工具类,通过字节码生成的方式加快反射速度
2)相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
26、说List,Set,Map三者的区别?
List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的
对象
Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象(HashSet 底层存储用的是hashMap在存储数据)。
Map(用Key来搜索的专家): 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相
同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
27。讲一下hashMap的底层实现
hashMap 是我们经常用到的集合,以Key-value形式保存数据,底层采用数组+链表(或者红黑树),HashMap的默认初始数组大小16(至于为什么是10 是因为需要减少hash碰撞,所以其数组大小始终是2的N次幂)
加载因子0.75
这是HashMap 的 链表与红黑树转换阈值:
/**
* 使用红黑树(而不是链表)来存放元素。当向至少具有这么多节点的链表再添加元素时,链表就将转换为红黑树。
* 该值必须大于2,并且应该至少为8,以便于删除红黑树时转回链表。
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 当桶数组容量小于该值时,优先进行扩容,而不是树化:
*/
static final int MIN_TREEIFY_CAPACITY = 64;
更具体一些,当put元素时,当链表满足上面阈值时会先put进第九个元素,在将链表转换成红黑树。