1.类的加载顺序
静态域:用staitc声明,jvm加载类时执行,仅执行一次。
构造代码块:类中直接用{}定义,每一次创建对象时执行。
执行顺序优先级:静态域( 静态变量、静态块和静态方法),main(),构造代码块,构造方法。
public class Test{public static Test t1 = new Test();//静态变量//构造块{System.out.println("blockA");}//静态块static{System.out.println("blockB");}public static void main(String[] args){Test t2 = new Test();}}//blockAblockBblockA
1. class StaticStuff2 {3. static int x = 10;4.5. static { x += 5; }6.7. public static void main(String args[])8. {9. System.out.println(“x = ” + StaticStuff .x);10. }11. static12. {x /= 3; }13.}//代码编译并执行,输出结果 x = 5.
(1) 父类静态对象和静态代码块
(2) 子类静态对象和静态代码块
(3) 父类非静态对象和非静态代码块
(4) 父类构造函数
(5) 子类非静态对象和非静态代码块
(6) 子类构造函数
public class B
{
public static B t1 = new B();
public static B t2 = new B();
{
System.out.println("构造块");
}
static
{
System.out.println("静态块");
}
public static void main(String[] args)
{
B t = new B();
}
}
//构造块 构造块 静态块 构造块
在调用子类构造器之前,会先调用父类构造器,当子类构造器中没有使用”super(参数或无参数)”指定调用父类构造器时,是默认调用父类的无参构造器,如果父类中包含有参构造器,却没有无参构造器,则在子类构造器中一定要使用“super(参数)”指定调用父类的有参构造器,不然就会报错。
class Base{
public Base(String s){
System.out.print("B");
}
}
public class Derived extends Base{
public Derived (String s) {
// super("s");
System.out.print("D");
}
public static void main(String[] args){
new Derived("C");
}
}
//编译错误
多态
class Base
{
public void method()
{
System.out.println("Base");
}
}
class Son extends Base
{
public void method()
{
System.out.println("Son");
}
public void methodB()
{
System.out.println("SonB");
}
}
public class Test01
{
public static void main(String[] args)
{
Base base = new Son();
base.method();//Son
base.methodB();//base属于Base对象,调用methodB()时此对象里没有这个方法,故编译不通过。通过SON son=(SON)base强制转换,然后用son.methodB()调用就可以了。向上转型,父类的引用无法访问子类独有的方法
}
}
//编译不通过
类是单继承的,接口是可以多继承的。
Java的基本编程单位是类,基本存储单元是变量。
2.Map
collection类型的集合(ArrayList,LinkedList)只能装入对象类型的数据。
没有使用泛型,取出的元素的编译类型是Object型的,但运行时类型是具体添加的类型。



new java.util.Map().put("key" , "value") ;//错
new java.util.SortedMap().put("key" , "value") ;//错
new java.util.HashMap().put( null , null ) ;
new java.util.TreeMap().put( 0 , null ) ;
Arrays.asList() 将一个数组转化为一个List对象,返回一个ArrayList类型的对象, 这个ArrayList类并非java.util.ArrayList类,而是Arrays类的静态内部类!
3.变量与方法



关键字
48个关键字:abstract、assert、boolean、break、byte、case、catch、char、class、continue、default、do、double、else、enum、extends、final、finally、float、for、if、implements、import、int、interface、instanceof、long、native、new、package、private、protected、public、return、short、static、strictfp、super、switch、synchronized、this、throw、throws、transient、try、void、volatile、while。
2个保留字(现在没用以后可能用到作为关键字):goto、const。
3个特殊直接量:true、false、null。
二维数组初始化
C语言中的二维数组,第二个控制列必须要有值,行可以不要。
Java中二维数组定义,一维的长度前面那个框值必须给。
float f[][] = new float[6][6];
float []f[] = new float[6][6];
float f[][] = new float[][6];//×
float [][]f = new float[6][6];
float [][]f = new float[6][];
三大基本特征:封装、继承、多态
五大基本原则:单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)
变量
定义在类中的变量是类的成员变量,可以不进行初始化,Java会自动进行初始化,如果是引用类型默认初始化为null,如果是基本类型例如int则会默认初始化为0。
局部变量是定义在方法中的变量,必须要进行显示初始化,否则不同通过编译,不能被控制修饰符及 static修饰。
final变量,如果是基本数据类型,则其数值一旦初始化后就不能被改变。如果是引用类型的变量,则对其初始化后,便不能再指向另一个对象,但是其里面的值是可以改变的。引用变量所指向的对象中的内容是可以改变的。final 方法可以被子类继承, 可以被重载 但不能被重写。final 类不能被继承,没有类能够继承 final 类的任何特性。
public class foo{
public static void main (String[] args){
String s;
System.out.println("s=" + s);//由于String s没有初始化,代码不能编译通过
}
}
static关键字可以修饰变量,方法,静态代码块。
静态变量:
由static修饰的变量称为静态变量
静态变量属于类,而不属于某个对象
静态变量它的副本只有一个(静态变量在类中只加载一次)
静态方法:
在静态方法中可直接调用静态变量和静态方法,可以有实例方法,可通过实例化对象,参考main方法
在非静态方法中,可以调用静态方法或者变量。
在静态方法中不能使用this和super关键字。
静态代码块:
用来给静态成员变量初始化
null可以被强制转换为任何引用类型;不能用一个值为null的引用类型变量来调用非静态方法,这样会抛出空指针异常,但是静态方法可以被一个值为null的引用类型变量调用而不会抛出空指针异常。
public class Test {
private static void print(){
System.out.println("Print()");
}
public static void main(String[] args) {
((Test)null).print();
}
}
//1.去掉static,这里不会编译错误,但是运行时会抛出空指针异常
//2.能编译通过,并且输出Print()
public static class Test {
public static String test(){
return "Java后端技术栈";
}
public static void main(String[] args) {
System.out.println(test());
}
}
//一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。
main方法也是static方法,所以只能访问静态变量,不能访问非静态变量a,解决办法将数组a声明为静态数组;在main方法new一个Arraytest的实例,然后通过对象去调用非静态变量。然后数组的初始值为0不是null。
public class Arraytest{
int a[] = new int[6];
public static void main ( String arg[] ) {
System.out.println ( a[0] );
}
}
//编译出错
public class Test {
public int aMethod(){
static int i = 0;
i++;
return i;
}
public static void main(String args[]){
Test test = new Test();
test.aMethod();
int j = test.aMethod();
System.out.println(j);
}
}
//编译失败
构造函数
与类名相同、 有返回值 类型,也不写void、 可以重载。
子类即使没有显示构造函数,也会有个无参数的默认构造函数,仍然会调用父类的构造函数。
子类的构造器第一行默认都是super(),默认调用直接父类的无参构造,一旦直接父类没有无参构造,那么子类必须显式的声明要调用父类或者自己的哪一个构造器。
构造方法不是类的成员方法。
构造方法不能被static、final、synchronized、abstract、native修饰,但可以被public、private、protected修饰。
构造方法可以被重载,但不能被继承。一个构造方法可以通过this关键字调用另一个构造方法,this语句必须位于构造方法的第一行。
如果子类没有重写父类的方法,调用父类的方法用不用super关键字结果都一样。 如果子类重写父类的方法,调用父类的方法必须用super关键字。 未被重写的方法可以直接调用。
class Person {
String name = "No name";
public Person(String nm) {
name = nm;
}
}
class Employee extends Person {
String empID = "0000";
public Employee(String id) {
empID = id;
}
}
public class Test {
public static void main(String args[]) {
Employee e = new Employee("123");
System.out.println(e.empID);
}
}
//编译报错
调用的方法是实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法。 
重写与重载
重写(Overriding)是父类与子类之间多态性的一种表现, 重载是一个类中多态性的一种表现。
重写要满足的条件
1.参数列表、返回类型、方法名要完全相同
2.子类抛出异常小于等于父类方法抛出异常
3.子类访问权限大于等于父类方法访问权限
重载要满足的条件
1.参数类型不同
2.参数个数不同
3.参数顺序不同
值传递与引用传递
class Value{
public int i=15;
}
public class Test{
public static void main(String argv[]){
Test t=new Test( );
t.first( );
}
public void first( ){
int i=5;
Value v=new Value( );
v.i=25;
second(v,i);
System.out.println(v.i);
}
public void second(Value v,int i){
i = 0;
v.i = 20;
Value val = new Value( );
v = val;
System.out.println(v.i+" "+i);
}
}
//15 0 20

package algorithms.com.guan.javajicu;
public class Inc {
public static void main(String[] args) {
Inc inc = new Inc();
int i = 0;
inc.fermin(i);
i= i ++;
System.out.println(i); //0
}
void fermin(int i){
i++;
}
}
public class Test2
{
public void add(Byte b)
{
b = b++;
}
public void test()
{
Byte a = 127;
Byte b = 127;
add(++a);
System.out.print(a + " ");//-128
add(b);
System.out.print(b + "");//127
}
}


char chr = 127;
int sum = 200;
chr += 1;
sum += chr;
//c/c++ 72 java 328
4.装拆箱
parseInt()是把String 变成int的基础数据类型; Valueof()是把String 转化成Integer对象类型; intValue()是把Integer对象类型变成int的基础数据类型。
a = Integer.parseInt("1024");
b = Integer.valueOf("1024").intValue();
//a和b都是整数类型变量并且它们的值相等。
Boolean flag = false;//调用Boolean.valueOf(boolean b)返回false对应的Boolean对象
if (flag = true)//1.自动装箱,2.自动拆箱
{
System.out.println("true");//true
}
else
{
System.out.println("false");
}
Byte,Short,Integer,Long,Character类型在-128—>127范围之间是被缓存了的,也就是每个对象的内存地址是相同的,赋值就直接从缓存中取,不会有新的对象产生,而大于这个范围,将会重新创建一个Integer对象,也就是new一个对象出来,当然地址就不同了。
Float和Double则不相等, Boolean的值总是相等的。
Integer a = 1;
Integer b = 1;
Integer c = 500;
Integer d = 500;
System.out.print(a == b);//true
System.out.print(c == d);//false
Integer s=new Integer(9);
Integer t=new Integer(9);
Long u=new Long(9);
System.out.print(s==u);//false
System.out.print(s==t);//false
System.out.print(s.equals(t));//true
System.out.print(s.equals(9));//true
System.out.print(s.equals(new Integer(9));//true

- int与Integer、new Integer()进行==比较时,结果永远为true,因为会把Integer自动拆箱为int再去比
- Integer与new Integer()进行==比较时,结果永远为false, 进行equals比较的话肯定为true
- 两个都是new出来的==为false,进行equals比较的话肯定为true
- Integer与Integer进行==比较时,看范围;在大于等于-128小于等于127的范围内为true,在此范围外为false。

ceil大于等于 x,并且与它最接近的整数;floor小于等于 x,且与 x 最接近的整数;round 则是4舍5入的计算, 即将原来的数字加上0.5后再向下取整。
double d1=-0.5;
System.out.println("Ceil d1="+Math.ceil(d1));//Ceil d1=-0.0
System.out.println("floor d1="+Math.floor(d1));//floor d1=-1.0
Math.floor(11.5)//12
Math.round(-11.5)//-11
Math.ceil(11.3)//12
Math.ceil(-11.6)//-11
Math.floor(11.6)//11
Math.floor(-11.4)//-12
Math.cos为计算弧度的余弦值,Math.toRadians函数讲角度转换为弧度, toDegrees()是将弧度转换为角度。
5.修饰符
final关键字可以修饰类、方法、变量。被final修饰的类是一个最终类,不可以被继承;被final修饰的方法是一个最终方法,不可以被覆盖,但是可以被继承;被final修饰的变量只能是一个常量,只能赋值一次。内部类被定义在类中的局部位置上时,只能访问局部被final修饰的局部变量。
abstract关键字只能描述类和方法,不能描述变量;抽象类只能被继承, 可以实现接口,不可以被实例化,可以含有非抽象方法、静态方法与构造方法;抽象方法不能有方法体,抽象方法只能定义在抽象类中。abstract关键字不可以与final、private、static关键字共存。 一个类继承于一个抽象类,则子类必须实现父类的抽象方法。
在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的。修饰符是默认的,不写等价, 接口中不能有构造方法、静态方法 。JDK8及以后,允许我们在接口中定义static方法和default方法, 也可以有方法体。
实现接口是需要重写接口中的abstract方法,一旦实现的类没有重写完,这个类必须是个抽象类。
关于抽象类
JDK 1.8以前,抽象类的方法默认访问权限为protected
JDK 1.8时,抽象类的方法默认访问权限变为default
关于接口
JDK 1.8以前,接口中的方法必须是public的
JDK 1.8时,接口中的方法可以是public的,也可以是default的
JDK 1.9时,接口中的方法可以是private的 
private修饰符定义的属性及方法不能被子类实现。
super 显示的告诉子类,当前的属性或方法来源于父类。可以使用super访问父类被子类隐藏的变量或覆盖的方法;每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错;构造是不能被继承的。
java.awt.是导入java\awt包下所有的类,并不包括其子包下的类。
java.awt**.event.才能导入java\awt\event包下的类。**
6.字符串
编译器优化:对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化。
Java对String的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”,然后将值为”taobao”的StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。
intern()先检查String池(或者说成栈内存)中是否存在相同的字符串常量,如果有就返回。
public class StringDemo{
private static final String MESSAGE="taobao";
public static void main(String [] args) {
String a ="tao"+"bao";
String b="tao";
String c="bao";
System.out.println(a==MESSAGE);//true
System.out.println((b+c)==MESSAGE);//false
System.out.println((b+c).intern()== MESSAGE);//true
}
}
public class StringDemo{
private static final String MESSAGE="taobao";
public static void main(String [] args) {
String a ="tao"+"bao";
final String b="tao";
final String c="bao";
String d = new String("taobao");
System.out.println(d==MESSAGE);//false
d = d.intern();
System.out.println((b+c)==MESSAGE);//true
System.out.println(d==MESSAGE);//true
System.out.println(a==a.intern());//true
}
}
replaceAll方法的第一个参数是一个正则表达式,而”.”在正则表达式中表示任何字符,所以会把前面字符串的所有字符都替换成”/“。
public static void main (String[] args) {
String classFile = "com.jd.". replaceAll(".", "/") + "MyClass.class";
System.out.println(classFile);// ////////MyClass.class
}
String确实是个不可变对象,直接赋值而不是使用new关键字给字符串初始化,在编译时就将String对象放进字符串常量池中;使用new关键字初始化字符串时,是在堆栈区存放变量名和内容;字符串的拼接操作在程序运行时,才在堆中创建对象。
String str1 = "java";
String str2 = "java";
System.out.print(str1==str2);// true
String str1 = new String("java");
String str2 = new String("java");
System.out.print(str1==str2);// false
String str1 = "java"; //直接赋值而不是使用new关键字给字符串初始化,在编译时就将String对象放进字符串常量池中
String str2 = "blog"; //在编译时就将String对象放进字符串常量池中
String s = str1+str2; //字符串的拼接操作在程序运行时,才在堆中创建对象,
System.out.print(s=="javablog");//false
String s1 = "java";
String s2 = new String("java");
System.out.print(s1.intern()==s2.intern());//true
String str1 = "java";
String str2 = new String("java");
System.out.print(str1.equals(str2));//true

String str = "hello world";
str+=' a';//str += 'a' 和 str +="a"都是对的,但是如果a前面加一个空格,那么只能用双引号了
int strlen = str.length;//数组有length属性,字符串只有length()方法
str=100;//int 无法直接转成String类型
str=str+100;//尾部添加字符串”100“
String、StringBuffer、StringBuilder
String是final修饰的,不可变。StringBuffer 有 synchronized 修饰,是线程安全的,所以性能比 StringBuilder 稍低一些。运行速度StringBuilder>StringBuffer>String。
String引用指向的地址是可变的,但是地址里的内容是不可变的。每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
如果字符串长度没有初始化长度大,capacity返回初始化的长度;如果append后的字符串长度超过初始化长度,capacity返回增长后的长度。 length 返回当前长度。 StringBuffer和StringBuilder的默认大小为16。
StringBuffer s1=new StringBuffer(10);
s1.append("1234");
s1.length();//4
s1.capacity();//10
String要设计成不可变?
字符串常量池的需要: Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象。
允许String对象缓存HashCode: 不必每次都去计算新的哈希码,字符串不变性保证了hash码的唯一性。
安全性:String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。
length()返回 char数组大小。 getBytes(“GBK”).length返回字节数
public class Test {
public static void main(String args[]) {
String s = "祝你考出好成绩!";
System.out.println(s.length());//8
}
}
//一个汉字等于一个字符、一个汉字GBK二个字节、UTF-8则是3个字节
public static String toString(char c) { return String.valueOf(c); }
public static String valueOf(int i) { return Integer.toString(i); }
public static String valueOf(char c) { char data[] = {c}; return new String(data, true); }
public class CharToString {
public static void main(String[] args)
{
char myChar = 'g';
String myStr = Character.toString(myChar);
System.out.println("String is: "+myStr);//g
myStr = String.valueOf(myChar);
System.out.println("String is: "+myStr);//g
}
}
substring 方法将返回一个包含从 start 到最后(不包含 end )的子字符串的字符串。
String a="Hello";
String b=a.substring(0,2); //He
"hamburger".substring(4, 8) returns "urge"
"smiles".substring(1, 5) returns "mile"
split这个方法默认返回一个数组, 如果没有找到分隔符会把整个字符串当成一个长度为1的字符串数组。
String str = "";
System.out.print(str.split(",").length);//1
String str1 = "12,3";
String str2 = "123";
System.out.print(str1.split(",").length);//2
System.out.print(str2.split(",").length);//1
7.进制
每个选项按照如下进行转换
//以下哪个式子有可能在某个进制下成立
13*14=204 √
12*34=568
14*14=140
1+1=3
按位且& 、按位或| 、按位取反~ 、按位异或^
—————————————————
逻辑与&& 、逻辑或|| 、非!
—————————————————
左移<<:补0,相当于乘以2;右移>>:补符号位,相当于除以2;无符号右移>>>:补0
public static void main(String args[]) {
System.out.println(14^3);//13
}
常见字符的ASCII码值如下:空格的ASCII码值为32;数字0到9的ASCII码值分别为48到57;大写字母“A”到“Z”的ASCII码值分别为65到90;小写字母“a”到“z”的ASCII码值分别为97到到122。
public class Test {
private static int j = 0;
private static Boolean methodB(int k) {
j += k;
return true;
}
public static void methodA(int i) {
boolean b;
b = i < 10 | methodB(4);//先判断条件1,不管条件1是否可以决定结果(这里决定结果为true),都会执行条件2
b = i < 10 || methodB(8);//先判断条件1,如果条件1可以决定结果(这里决定结果为true),那么就不会执行条件2
}
public static void main(String args[]) {
methodA(0);
System.out.println(j);
}
}
//The program prints”4”
8.类型转换
java是面向对象的,但是不是所有的都是对象,基本数据类型就不是对象,所以才会有封装类的。
小数默认是double类型, 整数默认是int类型。
byte a1 = 2, a2 = 4, a3;
a3 = a1 * a2; //因为byte,char,short会自动转为int,这里必须强制转换为byte
short s=1;s=s+1;//编译错误
short s=1;s+=1;正确
float f = 4.0;//float x = 4.0f或者float x = (float)4.0
Double d = 100;//double d=100
- byte,char,short,进行+、-、、/的时候都会转成int型进行运算。但是+=、-=、=、/=不会转成int后运算,而是直接运算。
- 若参与运算的数据类型不同,则先转换成同一类型,然后进行运算。
- 转换按数据长度增加的方向进行,以保证精度不降低。例如int型和long型运算时,先把int量转成long型后再进行运算。
- boolean类型不能和任何类型进行转换,会报出类型异常错误。
- 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
- 低级向高级是隐式类型转换,高级向低级必须强制类型转换。byte<char<short<int<long<float<double


基本类型之间的比较,应该会将低精度类型自动转为高精度类型再比较。
class CompareReference{
public static void main(String [] args){
float f=42.0f;
float f1[]=new float[2];
float f2[]=new float[2];
float[] f3=f1;
long x=42;
f1[0]=42.0f;
}
}
//x==f1[0] true
//f1==f3 true
public static void main(String[] args){
int i=42;
double d=42.0000;
long l=42;
float f=42.0f;
float f2=42.00f;
System.out.println(d==i);//true
System.out.println(f==i);//true
System.out.println(f==f2);//true
System.out.println(l==i);//true
System.out.println(d==f);//true
}

三元操作符
public static void main(String[] args) {
Object o1 = true ? new Integer(1) : new Double(2.0);
Object o2;
if (true) {
o2 = new Integer(1);
} else {
o2 = new Double(2.0);
}
System.out.print(o1);//1.0
System.out.print(" ");
System.out.print(o2);//1
}
// 三元操作符如果遇到可以转换为数字的类型,会做自动类型提升。
9.异常
throwable作为异常父类包括error和exception。
error类异常主要是运行时逻辑错误导致,一个正确程序中是不应该出现error的。当出现error一般jvm会终止。
exception表示可恢复异常,包括检查异常和运行时异常。 检查异常是最常见异常比如 io异常sql异常,都发生在编译阶段。这类通过try、catch捕捉;而运行时异常编译器没有强制对其进行捕捉和处理。一般都会把异常向上抛出,直到遇到处理代码位置,若没有处理块就会抛到最上层。常见的运行异常包括空指针异常、类型转换异常、数组越界异常、数组存储异常、缓冲区溢出异常、算术异常等。
- try…catch…finally finally不管什么异常都会执行
- 当try和catch中有return时,finally仍然会执行
- finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值
- 如果try,finally语句里均有return,忽略try的return,而使用finally的return
finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的
public class Test { public static void main(String[] args) { System.out.println(test()); } private static int test() { int temp = 1; try { System.out.println(temp);//输出try里面的初始temp:1 return ++temp;//保存return里面temp的值:2 } catch (Exception e) { System.out.println(temp); return ++temp; } finally { ++temp; System.out.println(temp);//执行finally的语句temp,返回try中的return语句 } } } //1,3,2finally块在以下几种情况将不会执行。
(1)finally块中发生了异常
(2)程序所在线程死亡
(3)在前面的代码中用了System.exit
(4)关闭了CPU
Throwable的子类:
1.Exception(异常):是程序本身可以处理的异常。
2.Error(错误):是程序无法处理的错误。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,一般不需要程序处理。
3.检查异常(编译器要求必须处置的异常): 除了Error,RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
4.非检查异常(编译器不要求处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。10.正则表达式
^表示匹配输入的开始,$表示匹配输入的结束
- ?表示匹配某元素0次或1次
- +表示匹配某元素1次或多次
- *表示匹配某元素0次或多次
- {n}表示出现n次、 {n,}表示出现n次或n次以上、 {n,m}表示出现n~m次
- .可以匹配除了换行符\n \r外的任何字符
- []表示字符集合,它用在正则表达式中表示匹配集合中的任一字符
- [a-zA-Z\d] 表示匹配一个小写字母 或者 大写字母 或者 数字
- \w 表示匹配字母数字或下划线。等价于[A-Za-z0-9_]
- \W匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]
- \d匹配一个数字字符。等价于 [0-9]
- \D匹配一个非数字字符。等价于 [^0-9]
- \s表示由空字符组成,[ \t\n\r\x\f]
- \S表示由非空字符组成,[^\s]
- ^7[0-9]表示匹配开头是7的,且第二位是任一数字的字符串 ```java 以下哪一项正则能正确的匹配网址: http://www.bilibili.com/video/av21061574
/^(https?:\/\/)?([a-zA-Z\d]+).bilibili.com\/?video\/av(\D{1,8})\/?$/ /^(http:\/\/)?(\w+).bilibili.com\/?video\/av(\d{1,8})\/?$/ √ /^(https?:\/\/)?(\w+).bilibili.com\/?\w$/ /^(http:\/\/)?([a-zA-Z\d]+).bilibili.com\/?video\/av\w\/+$/
```java
以下哪一个正则表达式不能与字符串“https://www.tensorflow.org/”(不含引号)匹配?
[a-z]+://[a-z.]+/
https[://]www[.]tensorflow[.]org[/] √
[htps]+://www.tensorflow.org/
[a-zA-Z.:/]+
11.内部类

public class OuterClass{
private float f=1.0f;
//插入代码到这里 A B C D 都是错的
}
//A
class InnerClass{
public static float func(){return f;}
}
//B
abstract class InnerClass{
public abstract float func(){}
}
//C
static class InnerClass{
protected static float func(){return f;}
}
//D
public class InnerClass{
static float func(){return f;}
}
为什么使用内部类?
- 它能够非常好的解决多重继承的问题。
- 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独。
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
分类
成员内部类
- Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等
- Inner 类中定义的 show() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,
如直接访问 Outer 类中的私有属性age - 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( )
- 成员内部类中不能存在任何 static 的变量和方法,可以定义常量
- 编译上面的程序后,会发现产生了两个 .class 文件: Outer.class,Outer$Inner.class{}
- 外部类是不能直接使用内部类的成员和方法的,可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法
如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字,如:Outer.this.name
public class Outer{ private int age = 99; String name = "Coco"; public class Inner{ String name = "Jayden"; public void show(){ System.out.println(Outer.this.name); System.out.println(name); System.out.println(age); } } public Inner getInnerClass(){ return new Inner(); } public static void main(String[] args){ Outer o = new Outer(); Inner in = o.new Inner(); in.show(); } }静态内部类:
创建静态内部类对象时不需要外部类的对象,可以直接创建内部类 对象名 = new 内部类()
- 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问
如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
public class Outer{ private int age = 99; static String name = "Coco"; public static class Inner{ String name = "Jayden"; public void show(){ System.out.println(Outer.name); System.out.println(name); } } public static void main(String[] args){ Inner i = new Inner(); i.show(); } }匿名内部类
直接使用 new 来生成一个对象的引用, 仅能被使用一次
- 继承一个类或者实现一个接口
- 不能定义构造函数,不能存在任何的静态成员变量和静态方法
利用构造代码块能够达到为匿名内部类创建一个构造器的效果
public class OuterClass { public InnerClass getInnerClass(final int num,String str2){ return new InnerClass(){ int number = num + 3; public int getNumber(){ return number; } }; /* 注意:分号不能省 */ } public static void main(String[] args) { OuterClass out = new OuterClass(); InnerClass inner = out.getInnerClass(2, "chenssy"); System.out.println(inner.getNumber()); } } interface InnerClass { int getNumber(); }局部内部类
作用域仅限于方法内,方法外部无法访问该内部类
- 不能有 public、protected、private 以及 static 修饰符
- 只能访问方法中定义的 final 类型的局部变量
- 在JDK8版本之中,方法内部类中调用方法中的局部变量,可以不需要修饰为 final,匿名内部类也是一样的,主要是JDK8之后增加了 Effectively final 功能
/*当方法被调用运行完毕之后,局部变量就已消亡了。但内部类对象可能还存在,直到没有被引用时才会消亡。此时就会出现一种情况,就是内部类要访问一个不存在的局部变量. 使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期. 局部内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数; 防止被篡改数据,而导致内部类得到的值不一致. */ public class Outer{ public void Show(){ final int a = 25; int b = 13; class Inner{ int c = 2; public void print(){ System.out.println("访问外部类:" + a); System.out.println("访问内部类:" + c); } } Inner i = new Inner(); i.print(); } public static void main(String[] args){ Outer o = new Outer(); o.show(); } }

12.IO
File类是java中文件和目录路径名的抽象表示形式。Java中对文件进行读写操作的基本类是IO类。
读到文件末尾不会抛出异常。EOFException:当输入过程中意外到达文件或流的末尾时,抛出此异常。
13.代码优化
代码优化也可分为局部优化、 循环优化和全局优化。
局部优化指的是在只有一个入口、 一个出口的基本程序块上进行的优化。
循环优化是对循环中的代码进行的优化,在一个程序运行时,相当多的一部分时间会花在循环上,因此,基于循环的优化非常重要。
全局优化是在整个程序范围内进行的优化。循环优化:对循环中的代码段,可以进行代码外提、强度削弱和删除归纳变量等优化。
删除多余运算:使生成的目标代码减少而执行速度较快。
死代码删除:**计算的结果决不被引用的语句。
14.JVM
JVM 内存可简单分为三个区:
堆区(heap):用于存放所有对象,是线程共享的(注:数组也属于对象)
栈区(stack):用于存放基本数据类型的数据和对象的引用,是线程私有的(分为:虚拟机栈和本地方法栈)
方法区(method):用于存放类信息、常量、静态变量、编译后的字节码等,是线程共享的(也被称为非堆,即 None-Heap),共享内存区域
Java 的垃圾回收器(GC)主要针对堆区
- 类加载器
启动类加载器(Bootstrap ClassLoader):负责加载存放在
扩展类加载器(Extension ClassLoader): 类加载器由sun.misc.Launcher$ExtClassLoader实现,负责加载
应用程序类加载器(Application ClassLoader):类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也称它为系统类加载器(System ClassLoader)。他负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器。
类只需加载一次就行,因此要保证类加载过程线程安全,防止类加载多次。
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类必定不相等。
- 双亲委派
Java程序的类加载器采用双亲委派模型,实现双亲委派的代码集中在java.lang.ClassLoader的loadClass()方法中,此方法实现的大致逻辑是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
- 堆内存
堆内存分为2块:Permanent Space 和 Heap Space。
- Permanent 即持久代(Permanent Generation),主要存放的是Java类定义信息,与垃圾收集器要收集的Java对象关系不大。
- Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年轻代(Young Generation)。年老代和年轻代的划分对垃圾收集影响比较大。
年轻代
所有新生成的对象首先都是放在年轻代。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代一般分3个区,1个Eden区,2个Survivor区(from 和 to)。
大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当一个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当另一个Survivor区也满了的时候,从前一个Survivor区复制过来的并且此时还存活的对象,将可能被复制到年老代。
2个Survivor区是对称的,没有先后关系,所以同一个Survivor区中可能同时存在从Eden区复制过来对象,和从另一个Survivor区复制过来的对象;而复制到年老区的只有从另一个Survivor区过来的对象。因为需要交换的原因,Survivor区至少有一个是空的。特殊的情况下,根据程序需要Survivor区是可以配置为多个的(多于2个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
针对年轻代的垃圾回收即 Young GC(Minor GC、Scavenge GC)。
年老代
在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
老年代也满了的话,就将触发Full GC,针对整个堆(包括新生代、老年代、持久代)进行垃圾回收。
持久代( 永久代、 方法区 )
持久代如果满了,将触发Full GC。
造成Full GC的原因:
1.年老代被写满
2.持久代被写满
3.显示调用System.GC
4.上一个GC后个heap的各域分配策略动态变化。
内存申请过程
- JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
- 当Eden区空间足够时,内存申请结束。否则执行下一步。
- JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区。
- Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代。
- 当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
- Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存,即出现“Out of Memory”。
OOM
- 年老代溢出,表现为:java.lang.OutOfMemoryError:Javaheapspace
这是最常见的情况,产生的原因可能是:设置的内存参数Xmx过小或程序的内存泄露及使用不当问题。
例如循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存。还有的时候虽然不会报内存溢出,却会使系统不间断的垃圾回收,也无法处理其它请求。这种情况下除了检查程序、打印堆内存等方法排查,还可以借助一些内存分析工具,比如MAT。 - 持久代溢出,表现为:java.lang.OutOfMemoryError:PermGenspace
通常由于持久代设置过小,动态加载了大量Java类而导致溢出,将参数 -XX:MaxPermSize 调大(一般256m能满足绝大多数应用程序需求)。
参数说明
- -Xmx3550m:设置JVM最大堆内存为3550M。
- -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
- -XX:MaxPermSize=256m:设置持久代最大值为256M。
- -XX:PermSize=256m:设置持久代初始值为256M。
- -XX:NewSize=1024m:设置年轻代初始值为1024M。
- -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
- -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
- -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
- XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
- -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性
年轻代参数优先级
- 高优先级:-XX:NewSize/-XX:MaxNewSize
- 中优先级:-Xmn(默认等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
- 低优先级:-XX:NewRatio
推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定 NewSize/MaxNewSIze,而且两者相等,适用于生产环境。-Xmn 配合 -Xms/-Xmx,即可将堆内存布局完成。
static String str0="0123456789";
static String str1="0123456789";
String str2=str1.substring(5);
String str3=new String(str2);
String str4=new String(str3.toCharArray());
str0=null;
//在发生过一次FullGC后,上述代码在Heap空间(不包括PermGen)保留的字符数为15
- 方法区
方法区是广义上的概念,是一个定义、标准,可以理解为Java中的接口,在Jdk6、7方法区的实现叫永久代;Jdk8之后方法区的实现叫元空间,并从JVM内存中移除,放到了直接内存中;方法区是被所有方法线程共享的一块内存区域。
运行时常量池
运行时常量池一直是方法区的一部分,存放编译期生成的各种字面量和符号引用;运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或者字段引用。 此时不再是常量池中的符号地址了,这里换为真实地址。
字符串常量池
JDK1.6时字符串常量池,被存放在方法区中(永久代),而到了JDK1.7,因为永久代垃圾回收频率低;而字符串使用频率比较高,不能及时回收字符串,会导致导致永久代内存不足,就被移动到了堆内存中。
堆内存分为2块:Permanent Space 和 Heap Space。
- 垃圾回收
对象的内存在哪个时刻回收,取决于垃圾回收器何时运行。
一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法, 并且在下一次垃圾回收动作发生时,才会真正的回收对象占用的内存。
在C++中,对象的内存在哪个时刻被回收,是可以确定的,在C++中,析构函数和资源的释放息息相关,能不能正确处理析构函数,关乎能否正确回收对象内存资源。
3种选择:串行收集器、并行收集器、并发收集器。串行收集器只适用于小数据量的情况,所以生产环境的选择主要是并行收集器和并发收集器。
默认情况下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行智能判断。
串行收集器
- -XX:+UseSerialGC:设置串行收集器。
- 适用于小数据量
并行收集器
- 吞吐量优先
- -XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。
- -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。此值建议配置与CPU数目相等。
- -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0开始支持对年老代并行收集。
- -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
- -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
并发收集器
- 响应时间优先
- -XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。CMS收集是JDK1.4后期版本开始引入的新GC算法。它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
- -XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
- -XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
- -XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
- -XX:+UseParNewGC:设置年轻代为并发收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此参数。
- -XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
其他垃圾回收参数
- -XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。
- -XX:-DisableExplicitGC:不响应 System.gc() 代码。
- -XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。
- -XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
- -XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。
复制算法和标记清理算法
- 栈
线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
- 程序计数器
当前线程所执行的字节码的信号指示器(偏移地址), 每一个线程都有一个独立的程序计数器(程序计数器的内存空间是线程私有的),因为当执行语句时,改变的是程序计数器的内存空间,因此它不会发生内存溢出 ,并且程序计数器是jvm虚拟机规范中唯一一个没有规定 OutOfMemoryError异常的区域。
- 命令
1、jps:查看本机java进程信息。
2、jstack:打印线程的栈信息,制作线程dump文件。
3、jmap:打印内存映射,制作堆dump文件
4、jstat:性能监控工具
5、jhat:内存分析工具
6、jconsole:简易的可视化控制台
7、jvisualvm:功能强大的控制台
辅助参数
- -XX:-CITime:打印消耗在JIT编译的时间。
- -XX:ErrorFile=./hs_err_pid.log:保存错误日志或数据到指定文件中。
- -XX:HeapDumpPath=./java_pid.hprof:指定Dump堆内存时的路径。
- -XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存。
- -XX:OnError=”;”:出现致命ERROR后运行自定义命令。
- -XX:OnOutOfMemoryError=”;”:当首次遭遇内存溢出时执行自定义命令。
- -XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆内存中类实例的柱状信息,同JDK的 jmap -histo 命令。
- -XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印线程栈中并发锁的相关信息,同JDK的 jstack -l 命令。
- -XX:-PrintCompilation:当一个方法被编译时打印相关信息。
- -XX:-PrintGC:每次GC时打印相关信息。
- -XX:-PrintGCDetails:每次GC时打印详细信息。
- -XX:-PrintGCTimeStamps:打印每次GC的时间戳。
- -XX:-TraceClassLoading:跟踪类的加载信息。
- -XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
- -XX:-TraceClassResolution:跟踪常量池。
- -XX:-TraceClassUnloading:跟踪类的卸载信息。
- 参数调优
1.承受海量访问的动态Web应用
服务器配置:8 CPU, 8G MEM, JDK 1.6.X
参数方案:
-server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio=6 -XX:MaxPermSize=256m -XX:ParallelGCThreads=8 -XX:MaxTenuringThreshold=0 -XX:+UseConcMarkSweepGC
2.高性能数据处理的工具应用
服务器配置:1 CPU, 4G MEM, JDK 1.6.X
参数方案:
-server -XX:PermSize=196m -XX:MaxPermSize=196m -Xmn320m -Xms768m -Xmx1024m
15.泛型
创建泛型对象的时候,一定要指出类型变量T的具体类型。争取让编译器检查出错误,而不是留给JVM运行的时候抛出类不匹配的异常。
在编译阶段虚拟机会采取擦除机制,将所有的泛型都编译成为原生类型。虚拟机中没有泛型,只有普通类和方法。
好处
类型安全。 通过知道使用泛型定义的变量的类型限制,编译器可以在一个高程度上验证类型假设。
消除强制类型转换。 代码更加可读,并且减少了出错机会。
<? extends A> 上边界限定通配符,代表小于等于A的范围;<? super A> 下边界限定通配符,代表大于等于A的范围;<?> 任意类型,代表全部范围

