- 判断JVM运行在Client还是Server模式
- java中有哪些常用的包?
- java中常用的集合类
- classloader(类加载器)
- 创建对象方式
- 创建线程方式
- 序列化
- ThreadLocal(线程局部变量)
- java里线程为什么很’贵’?
- io流
- finally执行顺序问题
- error与exception
- 内部类
- java注意事项
- assert断言
- stream parallel
里面放置一些零碎的java知识/注意事项等.
判断JVM运行在Client还是Server模式
使用java -version
即可查看
java中有哪些常用的包?
java.lang
: 提供基础类,是默认导入的包.重要的有Runnable
,Object
,Math
,String
,StringBuffer
,System
,Thread
,Throwable
等java.util
: 提供集合框架,事件模型,日期时间,国际化和各种工具类java.io
: 通过文件系统,数据流和序列化提供系统输入与输出java.nio
: 异步iojava.net
: 提供网络相关类java.sql
java.awt
: 提供了创建界面与图像的类javax.swing
: 提供一组轻量级
组件,以使在不同工作平台上工作方式相同java.text
: 提供了与自然语言无关的方式来处理文本,日期,数字和消息的类和接口
java中常用的集合类
Collection
Set
: 不能包含重复元素HashSet
TreeSet
List
: 有序列表ArrayList
: 内部使用数组
存储,适合查询操作多,增删操作少的情况LinkedList
: 内部使用双向链表
存储,适合增删操作多,查询操作少的情况Vector
: 内部使用数组存储,线程安全Stack
Queue
Map
: 不能包含重复的键,但可以包含重复的值HashMap
: 内部使用哈希表实现,线程不安全,允许键或值为nullLinkedHashMap
: 有序,但遍历比HashMap慢
Hashtable
: 线程安全,但也因此效率低于HashMap,不允许键或值为nullConcurrentHashMap
: 线程安全,并且锁分离.内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hashtable,它们有自己的锁.只要多个修改操作发生在不同的段上,它们就可以并发进行.TreeMap
: 内部使用红黑树实现,不允许键为null
classloader(类加载器)
java默认提供的classloader
-
Bootstrap ClassLoader(启动类加载器)
: 加载核心类库,如rt.jar,resources.jar,charsets.jar等它不是java类,不继承自ClassLoader,底层由c++编写,嵌入到jvm内核中
Extension ClassLoader(扩展类加载器)
: 加载扩展类库,默认加载$JAVA_HOME/jre/lib/ext
目录下所有jarApp ClassLoader(系统类加载器)
: 加载应用classpath目录下所有class和jar文件自定义类加载器
: 必须继承自ClassLoader
加载原理
ClassLoader采用双亲委托
模型,使用自上而下的顺序来加载,这样方式的好处有:
- 避免
重复加载
,即父加载器已经加载了,子加载器就不需要重新加载一次. 安全考虑
,比如你没法实现个自定义的String类代替默认的
如何判断两个类是否相同
- 由同一类加载器加载
- 类名相同
创建对象方式
new
反射
: Constructor的newInstance()方法-
clone()
: 类必须实现Cloneable
接口,否则会抛CloneNotSupportedException
异常.clone方法会直接复制字段内容,进行
浅拷贝
. 并且构造函数(及其它的实例语句)不会被调用.Object的clone()方法默认是protected的,约定为子类覆盖此接口,并将方法修饰符改为public
序列化
: 实现Serializable
接口,调用ObjectInputStream的readObject()方法
其中第3,4条是克隆对象的方式
创建线程方式
- 继承
Thread
: 使用this
即可获得当前线程 - 实现
Runnable
: 优点是线程类只是实现了Runnable接口,还能继承其它的类,更加灵活.访问当前线程使用Thread.currentThread()
方法
序列化
如果想把对象的属性(不包括方法)保存到文件或数据库,或者通过网络/RMI进行传输,那么就需要序列化.
要序列化的类必须实现Serializable
接口,并且所有属性必须是可序列化,
或者给属性加transient
关键字表示是短暂的,这个属性就不会被序列化,如敏感的密码字段.
父类实现序列化后,子类也会自动实现自动化.
可以通过ObjectOutputStream
进行序列化,通过ObjectInputStream
进行反序列化.
是否所有对象都能序列化?
并非所有的对象都可以序列化,原因比如:
- 安全方面原因,比如一个对象有private的属性,但序列化后,这些属性值也会暴露
- 资源分配方面原因,如socker,thread类,如果可以序列化传输,但资源也无法重新分配
ThreadLocal(线程局部变量)
本质上就是每个线程都维护了一个map,map的key就是ThreadLocal对象,值就是我们set的那个值
java里线程为什么很’贵’?
- 线程的创建与销毁成本很高
- 线程本身占用较大内存,在java里一个线程至少会分配512k~1M的空间
- 线程的切换成本高,切换时需要保存线程上下文,然后执行系统调用.如果线程过多,甚至可能导致切换时间大于线程执行时间.
io流
字符流
: 类名一般以reader
或writer
结尾字节流
: 类名一般以stream
结尾转换流
: 将字节流转换为字符流InputStreamReader
OutputStreamWriter
finally执行顺序问题
如果try块里有return语句,则finally会在return执行完后,方法返回前执行,如:
//以下代码会先调用a(),再调用b(),然后返回
try {
return a();
}finally {
b();
}
error与exception
error与exception都继承自throwable,但error不需要catch
内部类
内部类(inner class)就是定义在一个类中的类
内部类的分类
- 静态内部类
- 非静态内部类
- 成员内部类
- 方法内部类
- 匿名内部类
内部类的作用?
- 内部类可以访问外部类的成员,包括private
- 内部类可以对同一包中的其他类隐藏
- 内部类可以实现java’多继承’
- 回调函数可以使用匿名内部类实现以避免大量代码
内部类与外部类的关系
对于非静态内部类,如果没有外部类的实例是无法创建内部类的
从实现上来说,内部类会被编译为外部类$内部类.class
,对于虚拟机来说,并没有内部类的概念,实际就是一个单独的类
非静态内部类的创建方式
- 在外部类中
new InnerClass()
new OuterClass().new InnerClass()
内部类为什么能访问外部类成员?
当外部类对象创建了内部类对象,内部类对象就获得了外部类对象的引用
注意事项
- 非静态内部类不能包含静态成员
- 创建外部类的时候不一定要创建内部类
- 匿名内部类没有构造方法,因为它没有类名
java注意事项
- 静态方法无法被覆盖
- char类型可以储存一个中文汉字,因为java中使用的编码是Unicode
- 方法可以重载(Overload)和重写(Override),他们都是实现多态的方式
- 里氏代换原则: 基类可以出现的地方,之类一定可以出现
- 逻辑比较中,
&
是逻辑与,&&
是短路与(在&&左边为false时,右边不会执行) -
逻辑比较中, |
是逻辑或,||
是短路或(在左边为true时,右边不会执行) - java中有8个基础类型: byte,short,int,long,float,double,char,boolean,其他像String,枚举等都是引用类型
assert断言
断言一般用在开发与测试环境中,主要用来调试. 为了保证效率,生产环境一般不开启.
开启关闭方法
- 开启断言添加启动参数:
-ea
- 关闭断言添加启动参数:
-da
(默认无效,因此这个启动参数可以忽略)
使用方法
assert condition;
assert condition:expr;
,expr用于断言失败后的提示
stream parallel
什么时候用?
parallel更耗资源,协调线程需要花很多时间,因此不是什么情况都适合的. 适合的情况有:
- 大量任务要处理,这些任务可并行,并且都很耗时间
- 不用并行时有性能问题
parallelStream是什么?
parallelStream其实就是一个并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度.