JAVA修仙升级一本通
一、基础
1.基础数据类型及应用
1.1 基本类型
数据类型 | 默认值 | 大小(字节=8位) | 声明 | 正负边界 |
byte | 0 | 1 | byte a = 100 | -2^7 /2^7-1 |
short | 0 | 2 | short s = 1000 | -2^15 /2^15-1 |
int | 0 | 4 | int a = 100000 | -2^31 /2^31-1 |
long | 0L | 8 | long a = 1000L | -2^63 /2^63-1 |
float | 0.0f | 4 | float f = 234.5f | |
double | 0.0d | 8 | double d=0.2d | |
char | ‘u0000’ | 2 | char letter = 'A' char letter = '\u0000' |
\u0000/ \uffff(65535) |
String | null | – | ||
boolean | false | 1 |
1.2 类型转换
(1)转换规则
低 -> 高 :byte,short,char—> int —> long—> float —> double
在把容量大的类型转换为容量小的类型时必须使用强制类型转换
浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入
(2)计算转换
short a = 1;
short b = 2;
那么 a+b 是什么类型?
答:在java的世界里,如果比int类型小的类型做运算,java在编译的时候就会将它们统一强转成int类型。当是比int类型大的类型做运算,就会自动转换成它们中最大类型那个。
所有的byte,short,char型的值在操作时将被提升为int型
(3)强转符号的作用域
- 低级到高级可以隐式转换,高级到低级必须显示转换。
- +=, -= 类似 (short)(xx + xx),自带了强制转换。
- 对于 short s1 = 1; s1 += 1;由于 += 是 java 语言规定的运算符,java 编译器会对它进行特殊处理,s1 = (short) (s1 + 1);因此 可以正确编译。
- 对于 short s1 = 1; s1 = s1 + 1; 由于 s1+1 运算时会自动提升表达式的类型,所以结果是 int 型,再赋值给 short 类型 s1 时,编译器将报告需要强制转换类型的错误。
- 与0.0f或0.0d作算术比较(>或<判断)是允许的,但不能直接判断相等==。
- 做大小比较时考虑使用Float或Double的compare (f1, f2)方法,或BigDecimal。
- 整数型包装类型应该使用equals方法做比较。
- 浮点型包装类型不能用equals或者flt.compareTo(another) == 0进行相等的比较(可以进行compareTo(another)的大小比较)。
- 进行相等的比较:计算绝对值是否小于1e-6:(Math.abs(a -10.0) < 1e-6)。
- 和NaN的大小比较 和Doube.NaN() 禁止进行比较,!= 一定返回true,其余一定返回false。
(5)常量符号的影响
1.3 封装类型缓存
- Integer 初始化-128~127的范围缓存。
- Boolean 全局缓存。
- Character,缓存范围'\u0000' 到 '\u007F'。
Integer i = 0;
Integer j = Integer.valueOf(0);
Integer k = Integer.valueOf("0");
Integer m = Integer.parseInt("0");
Integer n = new Integer(0);
System.out.println(i == j); // true
System.out.println(i == k); // true
System.out.println(i == m); // true
System.out.println(i == n); // false
Integer fst = 1;
Integer snd = new Integer(1);
System.out.println(fst == snd);//false
System.out.println(new Integer(1) == snd);//false
System.out.println(fst == Integer.valueOf(1));//true
System.out.println(fst == Integer.valueOf("1"));//true
System.out.println(fst == Integer.parseInt("1"));//true
Integer trd = 256;
System.out.println(trd == Integer.valueOf(256));//false
System.out.println(trd == Integer.valueOf("256"));//false
System.out.println(trd == Integer.parseInt("256"));//true parseInt 返回的是基础类型,基础类型可以使用`==`,比较的时候直接拆包了
1.4 字符串
(1)字符串缓存
- String对象是不变对象,在程序运行过程中可能用到多个具有相同值得String对象,jvm中使用String pool来优化这种情况。当有新的String对象要建立的时候,jvm先检查Pool中时候已经有具有相同值的String对象,如果有就把这个对象的引用传递给新建立的对象,如果没有,就新建立一个对象,并将它放到Pool中。
- intern() 方法(调用该方法返回一个字符串,内容与调用该方法的字符串的内容相同,但保证来源于缓冲池中,如果池中没有于该字符串相同的字符串就将该String对象放入池中,并且返回该对象的引用)
- 如果左侧全是常量:对于常量,编译时就直接存储它们的字面值而不是它们的引用,在编译时就直接将它们连接的结果提取出来变成了”abc” ,该语句在class文件中就相当于String s = “abc”,然后当JVM执行到这一句的时候, 就在String pool(字符串缓冲池)里找,如果没有这个字符串,就会产生一个。如果左侧存在引用:
String s1 = "abc";
String s2 = new String( "abc" ); // 2 个
/***************/
String s1 = new String( "abc" ); // 2 个, 先创建1个作为构造函数的参数,再创建一个作为新的对象
/***************/
String s1 = "abc";
String s2 = new String( "abc" ).intern(); // 1个
/***************/
String str1 = "aaa";
String str2 = "bbb";
String str3 = "aaabbb";
String str4 = "aaa" + "bbb";//1个,不会产生新的字符串对象
System.out.println(str3 == str4);//true
str4 = str1 + "bbb";//会产生新的字符串对象
System.out.println(str3 == str4);//false
str4 = str1 + str2;//会产生新的字符串对象
System.out.println(str3 == str4);//false
/***************/
final String str1 = "aaa";
final String str2 = "bbb";
String str3 = "aaabbb";
/*
* 因为str1与str2都定义成了常量,所以编译时就能确定,编译时就会将常量替换,等同于
* str4 = "aaa"+"bbb",因此不产生新对象
*/
String str4 = str1 + str2;
System.out.println(str3 == str4);//true
2.引用数据类型及应用
3.java基本语法
3.1 可变参数
3.2 控制语句
3.3 循环
3.4 泛型
(2)super
class Food{}
class Friut extends Food{}
class Apple extends Friut{}
Food food = new Food();
Friut friut = new Friut();
Apple apple = new Apple();
List<? extends Friut> list1 = new ArrayList<>();
List<? super Friut> list2 = new ArrayList<>();
1、list1.add(apple); // false 不允许加入
2、List1.add(friut); // false
3、List1.add(food); // false
4、list2.add(apple); // true
5、List2.add(friut); // true
6、List2.add(food); // false 必须为子类
7、List<Food> foods = new ArrayList(); list1 = foods; // list1 标识子类集合 false
8、List<Friut> foods = new ArrayList(); list1 = foods; // true
9、List<Apple> foods = new ArrayList(); list1 = foods; // true
10、List<Food> foods = new ArrayList(); list2 = foods; // 父类集合 true
11、List<Friut> foods = new ArrayList(); list2 = foods; // true
12、List<Apple> foods = new ArrayList(); list2 = foods; // false
3.5 异常体系
- Throwable是所有Error和Exception的超类。
- 只有Throwable的子类才能被 catch 和 throw
- Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出,对于这类错误导致的引用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误建议让程序终止。
- Exception类分为运行时异常和受检查异常,受检查异常要么用try catch捕获,要么用throws字句抛出给父类处理,负责编译报错。运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。
- java.io.FileNotFoundException 泄露文件系统结构和文件名列举
- java.util.jar.JarException 泄露文件系统结构
- java.util.MissingResourceException 资源列举
- java.security.acl.NotOwnerException 所有人列举
- java.util.ConcurrentModificationException 可能提供线程不安全的代码信息
- javax.naming.InsufficientResourcesException 服务器资源不足(可能有利于DoS攻击)
- java.net.BindException 当不信任客户端能够选择服务器端口时造成开放端口列举
- java.lang.OutOfMemoryError DoS
- java.lang.StackOverflowError DoS
- java.sql.SQLException 数据库结构,用户名列举
4.Lamda表达式
4.1 函数式接口:只有一个抽象方法的接口,存在以下规则:
接口可以被继承,衍生出子类接口,也可以某个方法作为参数类型使用,调用时,会优先匹配子类的函数式接口,但如果存在多个子类函数式接口的方法,调用时只传函数式接口实例,则无法类型推断出具体使用哪个方法,编译会报错(只能强转成对应函数式接口类型);建议同一个函数式接口只继承一个子类,调用父类函数式接口类型的方法时只能使用强转
public class Main {
public static void main(String[] args) {
consume((MyFunction) Object::toString);
consume((Function<Object, String>) Object::toString);
consume(Object::toString);
consume(null);
}
private static void consume(Function<Object, String> mapping) {
System.out.println("jdk mapping");
}
private static void consume(MyFunction mapping) {
System.out.println("my mapping");
}
private static void consume(MyFunction2 mapping) {
System.out.println("my mapping2");
}
public interface MyFunction2 extends MyFunction {
}
public interface MyFunction extends Function<Object, String> {
}
}
输出:
my mapping
jdk mapping
my mapping2
my mapping2
5.IO编程
6.正则表达式
7.java面向对象特性
7.1 继承
- 重写 (overload):子类对父类接口的重写,相同的返回值和形参 使用@override注解,用来检测是否是有效的正确覆写,但是不写不会编译时报错
- 隐藏 (hide):父类同名的成员变量和静态方法只会被隐藏(静态绑定导致),父类的非静态方法会被覆盖(动态绑定导致)
- 重载 (override):同类之间函数的不通返回值和形参
- 遮蔽 (shadow): 一个变量、方法或类可以分别遮蔽(shadow)在类内部具有相同名字的变量、方法或类。如果一个实体被遮蔽了,name就无法通过简单名引用到它。
(1)不在父类的构造函数调用子类的覆写方法
public class SeniorClass {
public SeniorClass() {
toString();
}
@Override
public String toString() {
return "IAmSeniorClass";
}
}
public class JuniorClass extends SeniorClass {
private String name;
public JuniorClass() {
super();
name = "JuniorClass";
}
@Override
public String toString() {
return name.toUpperCase();
}
}
(2)父类引用指向子类
7.2 抽象类和接口
(1)抽象类
- 可以有具体实现方法,抽象类可以有构造方法,接口中不能有构造方法
- 抽象方法用abstract,抽象方法必须可以被继承 public或者protected
- 如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类
- 不可以被实例化
(2)接口
- 可有成员变量且一定是 public static final
- 可有成员函数默认是 public abstract
- 在java8以后,接口中可以添加使用default或者static修饰的方法。default修饰方法只能在接口中使用,在接口中被default标记的方法为普通方法,可以直接写方法体。
练习题
class Swan {
public void fly() {
System.out.println("swan can fly ...");
}
public static void flyS() {
System.out.println("swan can flyS ...");
}
}
class UglyDuck extends Swan {
@Override
public void fly() {
System.out.println("ugly duck can't fly ...");
}
public static void flyS() {
System.out.println("ugly duck can't flyS ...");
}
}
class TestFly {
public static void main(String[] args) {
Swan swan = new Swan();
Swan uglyDuck = new UglyDuck();
swan.fly();
uglyDuck.fly();
swan.flyS();
uglyDuck.flyS();
}
}
class Father {
private int year;
public int getGood() {
System.out.println("useFatherGet");
return this.year;
}
public void setGood(int good) {
this.year = good;
System.out.println("useFatherSet");
}
@Override
public String toString() {
return this.year + "!!!";
}
}
class Son extends Father {
@Override
public int getGood() {
System.out.println("useSonGet");
return super.getGood();
}
public int getGood(int good) {
System.out.println("useSonGetOverWrite");
return super.getGood();
}
@Override
public void setGood(int good) {
super.setGood(good);
System.out.println("useSonSet");
}
}
class CodeSolution {
public static void main(String[] args) {
Son son = new Son();
son.setGood(11);
System.out.println(son.toString());
}
}
二、中级特性
1.java反射机制
2.java常用类库
2.1 集合
- ArrayList使用无参构造器创建的ArrayList初始容量为0(推荐),第一次调用add/addAll方法时才会初始化数组容量,初始容量为10。
- 对集合添加若干元素时,如果当前集合的容量满足需求则不扩容,如果不满足则扩大1.5倍,如果扩大1.5依然不满足则扩大为满足需求的最小容量。
- Vector、HashSet、HashMap 扩容增量:原容量的 1倍;ArrayList扩容增量:原容量的 0.5倍 + 1
2.2 哈希
- 父类不同:HashMap继承了AbstractMap,HashTable继承Dictionary抽象类,两者均实现Map接口;
- Hashtable不允许null值(包括键或值),HashMap允许一个空键(其他的空键会覆盖第一个空键)和任意数量的空值;
- Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别;所以就意味着Hashtable是线程安全的,Hashtable效率较低;HashMap不是线程安全的,HashMap效率较高;
- HashMap 存储原理:先比较hashcode。定位原理:先hashcode再equals
2.3 LinkedHashMap
关注点 | 结论 |
LinkedHashMap是否允许空 | Key和Value都允许空 |
LinkedHashMap是否允许重复数据 | Key重复会覆盖、Value允许重复 |
LinkedHashMap是否有序 | 有序 |
LinkedHashMap是否线程安全 | 非线程安全 |
2.4 子集合 SubList
3.java线程同步
4.java线程规范与管理
三、高级特性
1.垃圾回收
必备知识:
1.1 垃圾回收是什么
1.2 如何识别垃圾
(1)使用引用计数法识别垃圾
- 对象A、B、C不是垃圾
- 对象F是垃圾,引用计数为0,被回收
- 对象D、E是垃圾,但引用计数不为0,出出现内存泄漏
(2)使用可达性分析
- 方法栈使用到的参数、局部变量、临时变量等
- 方法区中类静态属性引用的变量
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- 对象A、B、C可以被遍历到,不是垃圾
- 对象D、E不会被遍历到,会被回收
- 目前主流虚拟机采用这种算法,包括Orace JDK、Huawei JDK等
1.3 如何执行垃圾回收(垃圾回收算法)
(1)清除(sweep)
(2)整理(compact,也叫压缩)
(3)复制(copy)
1.4 有哪些垃圾收集器
- 特点:发生次数多,采用时间短,回收掉大量对象
- 收集器:serial, Parallel Scavenge, Parallel New.均采用复制算法. Serial是单线程,Parallel New可以看成Serial多线程版本. Parallel Scanvenge和Parallel New类似,但更注重吞吐率,且不能与CMS一起使用
- 特点:发生次数少,耗时长
- 收集器:Serial Old(整理), Parallel Old(整理), CMS(清除). Serial Old是单线程的,Parallel Old可以看成Serial Old的多线程版本. CMS是并发收集器,除了初始标记和重新标记操作需要Stop the world,其它时间可以与应用程序一起并发执行
1.5 垃圾回收的触发条件
- 老年代空间不足
- 方法区(Metaspace)空间不足
- 通过minor GC进入老年代的平均大小大于老年代的可用内存
- 老年代被写满
- 调用System.GC,系统建议执行full GC,但不一定执行。 禁止使用主动GC(除非在密码、RMI等方面),尤其是在频繁/周期性的逻辑中
2.类加载
2.1 类加载过程
- 支持的文件类型 jar | war | ear | (java web enterprise)
- 字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译
- 类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。
- 验证:验证字节码信息是否符合jvm规范
- 准备:分配内存,并为静态变量赋初始值
- 解析:将常量池中的符号引用转换为直接引用。也可以在初始化之后再开始,来支持java的运行时绑定
- 执行静态初始化块(static{})和类变量赋值.先初始化父类,后初始化子类
- 不要在static块中抛出异常,否则会导致类初始化失败,抛ExceptionInInitializerError异常,进而导致其他异常
- 对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)> 构造器。
- 执行父类的静态代码块,并初始化父类静态成员变量
- 执行子类的静态代码块,并初始化子类静态成员变量
- 执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
- 执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量
类的卸载
- 该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
- 该类没有在其他任何地方被引用
- 该类的类加载器的实例已被 GC
2.2 ClassLoader层次结构
- 双亲委派:先由父类加载器加载,加载不到或者加载失败后再由子类加载器加载
- 如果Class文件不在父类的加载路径中,则由子类加载,如果仍然找不到,抛ClassNotFound异常
- 先加载JDK中的类,再加载用户的类
2.3 类初始化过程
- JVM启动时,先初始化用户指定的主类
- 初始化子类之前,先初始化父类
- 访问类的静态变量或静态方法
- 创建类实例
- 反射调用类
- JVM会加锁来保证类初始化只进行一次,
2.4 对象初始化顺序
- 父类静态代码块
- 子类静态代码块
- 父类代码块
- 父类构造函数
- 子类代码块
- 子类构造函数
2.5 JNI
有些事情Java无法处理时,JNI允许程序员用其他编程语言来解决,例如,Java标准库不支持的平台相关功能或者程序库。也用于改造已存在的用其它语言写的程序,供Java程序调用。许多基于JNI的标准库提供了很多功能给程序员使用,例如文件I/O、音频相关的功能。当然,也有各种高性能的程序,以及平台相关的API实现,允许所有Java应用程序安全并且平台独立地使用这些功能。JNI框架允许Native方法调用Java对象,就像Java程序访问Native对象一样方便。Native方法可以创建Java对象,读取这些对象,并调用Java对象执行某些方法。当然Native方法也可以读取由Java程序自身创建的对象,并调用这些对象的方法。
// 本地方法的正确定义方式
class HelloWorld {
public native void displayHelloWorld();
static {
System.loadLibrary("hello");
}
public static void main(String[] args) {
new HelloWorld().displayHelloWorld();
}
}
练习题
3. java编译与优化
3.1 javac编译器
3.2 java即时编译器(后端编译)
(1)为什么需要JIT即时编译器
(2)执行过程
(4)JIT即时编译器类型
Client Compiler(C1编译器)
- 局部简单可靠的优化,比如字节码上进行的一些基础优化,方法内联、常量传播等,放弃许多耗时较长的全局优化。
- 将字节码构造成高级中间表示(High-level Intermediate Representation,以下称为HIR),HIR与平台无关,通常采用图结构,更适合JVM对程序进行优化。
- 最后将HIR转换成低级中间表示(Low-level Intermediate Representation,以下称为LIR),在LIR的基础上会进行寄存器分配、窥孔优化(局部的优化方式,编译器在一个基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,通过一些认为可能带来性能提升的转换规则或者通过整体的分析,进行指令转换,来提升代码性能)等操作,最终生成机器码。
Server Compiler
C2编译器
在Hotspot VM中,默认的Server Compiler是C2编译器。C2编译器在进行编译优化时,会使用一种控制流与数据流结合的图数据结构,称为Ideal Graph。 Ideal Graph表示当前程序的数据流向和指令间的依赖关系,依靠这种图结构,某些优化步骤(尤其是涉及浮动代码块的那些优化步骤)变得不那么复杂。
Ideal Graph的构建是在解析字节码的时候,根据字节码中的指令向一个空的Graph中添加节点,Graph中的节点通常对应一个指令块,每个指令块包含多条相关联的指令,JVM会利用一些优化技术对这些指令进行优化,比如Global Value Numbering、常量折叠等,解析结束后,还会进行一些死代码剔除的操作。生成Ideal Graph后,会在这个基础上结合收集的程序运行信息来进行一些全局的优化,这个阶段如果JVM判断此时没有全局优化的必要,就会跳过这部分优化。
无论是否进行全局优化,Ideal Graph都会被转化为一种更接近机器层面的MachNode Graph,最后编译的机器码就是从MachNode Graph中得的,生成机器码前还会有一些包括寄存器分配、窥孔优化等操作。关于Ideal Graph和各种全局的优化手段会在后面的章节详细介绍。Server Compiler编译优化的过程如下图所示:
Graal编译器
从JDK 9开始,Hotspot VM中集成了一种新的Server Compiler,Graal编译器。相比C2编译器,Graal有这样几种关键特性:
前文有提到,JVM会在解释执行的时候收集程序运行的各种信息,然后编译器会根据这些信息进行一些基于预测的激进优化,比如分支预测,根据程序不同分支的运行概率,选择性地编译一些概率较大的分支。Graal比C2更加青睐这种优化,所以Graal的峰值性能通常要比C2更好。
使用Java编写,对于Java语言,尤其是新特性,比如Lambda、Stream等更加友好。
更深层次的优化,比如虚函数的内联、部分逃逸分析等。
Graal编译器可以通过Java虚拟机参数-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler启用。当启用时,它将替换掉HotSpot中的C2编译器,并响应原本由C2负责的编译请求。
(5)JIT编译器如何执行:分层编译
- 0.解释执行。
- 1.执行不带profiling的C1代码。
- 2.执行仅带方法调用次数以及循环回边执行次数profiling的C1代码。
- 3.执行带所有profiling的C1代码。
- 4.执行C2代码。
- 图中第①条路径,代表编译的一般情况,热点方法从解释执行到被3层的C1编译,最后被4层的C2编译。
- 如果方法比较小(比如Java服务中常见的getter/setter方法),3层的profiling没有收集到有价值的数据,JVM就会断定该方法对于C1代码和C2代码的执行效率相同,就会执行图中第②条路径。在这种情况下,JVM会在3层编译之后,放弃进入C2编译,直接选择用1层的C1编译运行。
- 在C1忙碌的情况下,执行图中第③条路径,在解释执行过程中对程序进行profiling ,根据信息直接由第4层的C2编译。
- 前文提到C1中的执行效率是1层>2层>3层,第3层一般要比第2层慢35%以上,所以在C2忙碌的情况下,执行图中第④条路径。这时方法会被2层的C1编译,然后再被3层的C1编译,以减少方法在3层的执行时间。
- 如果编译器做了一些比较激进的优化,比如分支预测,在实际运行时发现预测出错,这时就会进行反优化,重新进入解释执行,图中第⑤条执行路径代表的就是反优化。
(6)即时编译的触发时机
开启分层编译的情况下,-XX:CompileThreshold参数设置的阈值将会失效,触发编译会由以下的条件来判断:
- 方法调用次数大于由参数-XX:TierXInvocationThreshold指定的阈值乘以系数。
- 方法调用次数大于由参数-XX:TierXMINInvocationThreshold指定的阈值乘以系数,并且方法调用次数和循环回边次数之和大于由参数-XX:TierXCompileThreshold指定的阈值乘以系数时。
4.疑难问题定位
(1)JVM常用的调优命令
命令 | 说明 |
jps | JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。 命令格式 jps [options] [hostid] option参数 -l : 输出主类全名或jar路径 -q : 只输出LVMID -m : 输出JVM启动时传递给main()的参数 -v : 输出JVM启动时显示指定的JVM参数 其中[option]、[hostid]参数也可以不写。 |
jstat | jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 命令格式 jstat [option] LVMID [interval] [count] 参数 [option] : 操作参数 LVMID : 本地虚拟机进程ID [interval] : 连续输出的时间间隔 [count] : 连续输出的次数 option 参数总览 class class loader的行为统计。Statistics on the behavior of the class loader. compiler HotSpt JIT编译器行为统计。Statistics of the behavior of the HotSpot Just-in-Time compiler. gc 垃圾回收堆的行为统计。Statistics of the behavior of the garbage collected heap. gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计。Statistics of the capacities of the generations and their corresponding spaces. gcutil 垃圾回收统计概述。Summary of garbage collection statistics. gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因。Summary of garbage collection statistics (same as -gcutil), with the cause of the last and gcnew 新生代行为统计。Statistics of the behavior of the new generation. gcnewcapacity 新生代与其相应的内存空间的统计。Statistics of the sizes of the new generations and its corresponding spaces. gcold 年老代和永生代行为统计。Statistics of the behavior of the old and permanent generations. gcoldcapacity 年老代行为统计。Statistics of the sizes of the old generation. gcpermcapacity 永生代行为统计。Statistics of the sizes of the permanent generation. printcompilation HotSpot编译方法统计。HotSpot compilation method statistics. |
jmap | jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还阔以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。 命令格式 jmap [option] LVMID option参数 dump : 生成堆转储快照 finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象 heap : 显示Java堆详细信息 histo : 显示堆中对象的统计信息 permstat : to print permanent generation statistics F : 当-dump没有响应时,强制生成dump快照 |
jhat | jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。 命令格式 jhat [dumpfile] |
jstack |
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。 命令格式 jstack [option] LVMID option参数 -F : 当正常输出请求不被响应时,强制输出线程堆栈 -l : 除堆栈外,显示关于锁的附加信息 -m : 如果调用到本地方法的话,可以显示C/C++的堆栈 |
jinfo | jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令 命令格式 jinfo [option] [args] LVMID option参数 -flag : 输出指定args参数的值 -flags : 不需要args参数,输出所有JVM参数的值 -sysprops : 输出系统属性,等同于System.getProperties() |
四、实战应用
1.java安全管理器
- 是一个定义安全策略的对象,此策略指定不安全或敏感操作。
- 安全策略不允许的任何操作都会抛出SecurityException异常
- Java应用程序默认不启用SecurityManager
- 当SecurityManager检测到违反安全策略的操作时,将引用AccessControlExption或SecurityException
- 不要将AllPermission许可覆给不可信的代码
- 没有配置的权限表示没有。
- 只能配置有什么权限,不能配置禁止做什么。
- 同一种权限可多次配置,取并集。
- 同一资源的多种权限可用逗号分割。
2.JDBC数据库开发
3.Socket网络编程
4.实践编程基本规范
- 方法 50 行 ;类 2000 行 都是非空非注释的
- 嵌套不超过四层
- 建议 构造方法如果参数较多,尽量重用
- 建议 对于返回数组或者容器的方法,应返回长度为0的数组或者容器,代替返回null
- 在最低限度,Javadoc用于每一个 public或 protected 修饰的类、接口、枚举、方法和成员变量
- 变量命名不能与关键字冲突:public protected private abstract default static final transient volatile synchronized native strictfp
练习题:
- 点赞
- 收藏
- 关注作者
评论(0)