1. 当调用一个对象中的方法时,比如调用Dog类的实例dog的eat(),编译器会把dog引用当成eat()的第一个参数传入即(eat(this, otherParams))

  2. 在构造器中可以使用this关键字去显式的调用构造器,并且只能位于构造器的第一行,其他地方都不能调用构造器

  3. Java为类做的准备工作包括以下三步,获取类名.class、编译器常量(static final并且编译器就能确定值的字段)只会执行第一步加载,并不会进行链接和初始化,由于反射(Class.forName())是在运行时创建的Class对象因此通过该方法会进行链接和初始化

    1. 加载 ClassLoader在ClassPath下寻找到类的字节码文件后,根据字节码创建一个Class对象,也就是使用类名.class获取的那个对象
    2. 链接 验证类中的字节码,为静态域分配存储空间
    3. 初始化 如果该类有父类加载父类,然后初始化父类的静态域,然后回来初始化子类静态域
  4. 代码的执行顺序,创建对象执行1-5,调用静态字段、方法执行1-2

    1. 当Java运行时需要某个类时(调用该类的静态域或者构造器(其实其也属于静态方法),除去static final的编译器常量),会触发改类的加载->链接->初始化,如果其有父类则在子类初始化开始后,触发父类的加载->链接->初始化,以此类推
    2. 从基类到子类初始化静态字段和静态代码块按照书写顺序
    3. 在堆中分配一块空间,并把该空间都置于零,基本数据类型为0,引用为null
    4. 调用构造方法第一行直到Object
    5. 为基类的非静态字段进行初始化,然后执行剩余的构造方法。下图说明了这5点。
  5. 静态字段属于类,当类加载就会初始化,非静态字段属于对象,当对象不存在时是不会进行初始化的

  6. 垃圾回收机制

    • 引用计数法 每个对象都有一个引用计数器,当有引用连接到该对象引用计数器++,当引用离开作用域或者置为null引用计数器–,当垃圾回收器发现某个对象的引用计数器为0时就释放它,缺点:当对象相互引用的时候引用计数器不为0,但是两个对象都不再需要,这时候就没法释放了
    • 自适应的、分代的、停止-复制、标记-清扫式垃圾回收器。停止-复制,先暂停程序从GCRoot(静态区,堆栈引用)出发找到所有的活的对象,并且把这些对于一个紧挨一个放到新的一块内存中,然后更新堆栈中和静态区的引用然后删除原来占据的堆内存,缺点:需要第二块内存,并且当可回收的对象过少的时候效率不高 优点:整理了堆中的对象可以有更多空间来存储。标记-清扫,也是先暂停程序也从GCRoot出发找对象每找到一个活着的对象就把它标记,等所有的引用都遍历结束后,删除没有被标记的对象。分代的,每个内存块都有一个代数,大型对象不会被复制,内含小型对象的块则会被复制和整理。自适应的,虚拟机会跟踪标记-清扫的效果,要是堆空间中出现很多小碎片就会切换到停止-复制。
  7. 数组,数组初始化有以下两种,其中第一种只能在定义时初始化,数组分为基本数据类型数组和引用数组),数组本身也是一个引用(也是一个Object对象),可以通过调用Arrays.toString(arr)来打印数组

    int[] iArr = {1,2,3,4,5}; //1
    int[] bArr = new int[10]; //2Obj[] arrs = new Obj[10]; //创建了一个引用数组,并没有初始化里面的引用,因此都是null
    
  8. 可变参数,使用方式如下,必须位于方法的最后一个参数,就相当于一个数组

    static void print(Object... objects) { //可以传入0-n个Object对象或者是一个引用数组(如果传了基本数据类型数组,那么相当于只是传了一个Object对象)System.out.println(Arrays.toString(objects));
    }
    
  9. 枚举enum关键字,枚举也是一个类,只是产生了某些编译器行为,编译器会创建toString()、ordinal()方法,并且会给枚举类创建一个values()静态方法获取所有的实例,代码如下,并且枚举里面声明的枚举对象都是静态的

    enum PagerMoney {ONE_DOLLAR, FIVE_DOLLARS, TEN_DOLLARS, FIFTY_DOLLARS, ONE_HUNDRED_DOLLARS
    }for (PagerMoney money: PagerMoney.values()) { //返回一个PagerMoney数组,包含所有Dog类的实例System.out.println(money + " " + money.ordinal()); //该方法获取实例创建的顺序从0开始
    }
    /* outputs:
    ONE_DOLLAR 0
    FIVE_DOLLARS 1
    TEN_DOLLARS 2
    FIFTY_DOLLARS 3
    */
    
  10. 静态导入,当静态导入一个类后调用该类中的静态方法就可以不写类名了。

    import static com.hfw.utils.CommonUtils.*; //静态导入就不用写类名了public class TestUtils {public static void main(String[] args) {print("Hello,World");}
    }
    
  11. 包名,包名必须与文件目录一一对应,因为java解释器在加载类的时候会将包名的.替换成/,生成一个目录,然后在所有的CLASSPATH下面的这个目录(由于CLASSPATH中含有.因此也会查找当前位置)查找.class文件,所以如果包名与文件目录不对应那么java解释器将找不到文件。

  12. 访问控制,在创建类的时候最好把字段和方法按照public、protected、默认、private的顺序书写,外部类只有public和默认两种访问控制,内部类具有四种(如果设置为private则只有同属于一个最外部才能访问,其默认构造方法也是private,如果设置为protected,则其默认构造方法也是protected)也就是默认构造器的访问控制符与类的访问控制符一致

    • public 公开的,全局都可以访问被public修饰的类或者字段或者方法

    • protected 受保护的,同包、同类、子类可以访问该方法(如果从其他包继承的一个方法,并且该方法不是public和protected那么子类就会没法访问这个从父类继承的方法)

    • 默认 default,同包、同类中能访问

    • private 私有的 只能在同属于一个最外部类访问

  13. 继承 基本做法是基类把所有字段设置成private,而把所有方法设置为public。当创建一个子类对象时,其实会先创建一个父类对象,相当于一个子类对象中包含一个父类对象(解释了为什么子类对象不能调用父类对象的private字段和方法)并且在子类构造器中会首先调用父类构造器,父类构造器如果有参数则必须在子类构造器中明确使用super调用父类构造器(在父类构造器没走完之前是不会初始化子类的非静态的字段的,当父类构造器执行完后才初始化子类非静态字段,然后继续执行子类的构造器代码如下所示)

    package com.hfw.section7.practice;
    public class PracticeFive {public static void main(String[] args) {new CC();}
    }
    class AA {AA() {System.out.println("AA");}
    }
    class BB {BB() {System.out.println("BB");}
    }
    class CC extends AA {BB bb = new BB();CC() {System.out.println("CC");}
    }
    //outputs:
    AA
    BB
    CC
    
  14. 代码复用

    • 组合 一个类中包含另一个类的引用,需要时从该类获取引用调用其方法

    • 继承 调用时直接调用其方法

    • 代理 结合前两者,其拥有另一个类引用,并且创建对应的方法如f(),然后在该方法中调用另一个类对象的f(),idea可以自动生成代理方法

  15. final,当使用final修饰基本数据类型时(名称需要大写),如果数值是确定的那么就属于编译期常量可以减轻运行时的负担。一般基本数据类型常量定义为public static final,public表示其他包可以访问,static表示只有一份。对基本数据类型使用final那么它的值就不能发生变化,对引用使用final那么引用不能发生变化但是它所指向的对象的内容可以发生变化

    • 字段 表示其是一个常量。被final修饰的字段只能在定义时或者构造器中初始化,而一旦初始化就不能更改了。
    • 方法参数 在方法内部无法更改参数,常用与方法内部的匿名内部类
    • 方法 不可以被重写,基类的private方法隐含final,因为private方法不能被重写,子类就算写了一个与父类声明一样的方法也不是重写此外static方法也不能被重写
    • 类 不可以被继承
  16. 方法绑定 java中除了static、final(private也属于final)方法外,其他所有方法都是动态绑定(也就是在运行时绑定),多态的实现就是依赖动态绑定,在运行时确定调用的方法。

  17. 多态 子类可以向上转型成父类(将子类对象赋值给父类),并且在运行时程序能够正确调用方法,书写程序时最好只与基类进行通信而不依赖于某一个具体的实现,这样程序就是可扩展的。多态是一项将改变的事物与未变的事物分离开来的重要技术,当基类中需要增加一些其他方法时完全不影响原来的代码。注意点:1. 子类是无法重写父类的私有方法的,因此上转型到父类调用该方法会调用父类的方法(在子类中不要创建与父类的私有方法一样的方法) 2. 如果父类拥有一个字段A,子类也拥有字段A,那么将子类上转型到父类获取A得到的是父类的A字段,因为这个访问在编译器进行,如果采用调用方法获取则获取的是子类的A字段(也就是字段不属于多态)。举例如下。3. 静态方法不具有多态

    public class TestPolymorphism {static class Super {int field = 0;}static class Sub extends Super {int field = 1;int getField() {return field;}int getSuperField() {return super.field;}}public static void main(String[] args) {Super su = new Sub();System.out.println(su.field); //直接获取得到的是父类的0,编译时决定System.out.println(su.getField()); //方法调用得到的是子类的1,运行时决定}
    }
    
  18. 构造器 准则“用尽可能简单的方法使对象进入正常状态,如果可以的话避免调用其他方法,在构造器能安全的调用的方法只有当前类的final和private方法因为它们不能被继承”,如果不遵守可能会出现下面的问题。

    //会输出Circle print 0 因为在堆中创建了空间后所有值都被置于0,而在Shape构造器执行的时候Circle的成员变量还没赋值
    public class PracticeFifteen {public static void main(String[] args) {Circle circle = new Circle();}static class Shape {Shape() {print();}void print() {println("Shape print");}}static class Circle extends Shape {int radius = 2;Circle() {}void print() {println("Circle print" + radius); }}}
    
  19. 抽象类,如果一个类的父类不是抽现类,而该类声明了abstract并且没有任何抽象方法则该类不能被实例化

  20. 接口,接口的所有字段都是public static final的,方法都是public abstract的,所以要求实现类的方法也必须是public的。接口中的字段不能是空final的(声明的时候不赋值,而在构造器赋值)。 接口可以继承另一个接口(也可以继承多个接口中间用逗号分割),一个类可以实现多个接口中间用逗号分割。在写自己的类库时方法最好是需要传入一个实现了该类库中的接口的实例(声明为你可以用任何你想用的对象来调用我,只要你的对象遵循我的接口)

  21. 内部类 分为成员内部类、局部内部类、静态内部类(嵌套类,非内部类一律不能声明为static class),接口中定义的内部类一定是静态内部类,非静态内部类对象含有一个外部类引用,所有能够访问外部类的所有成员包括private字段、方法(可以通过外部类类名.this.xxx来明确使用外部类的xxx)。要创建一个内部类对象必须要先有一个外部类对象(假设外部类名为Outer对象为outer内部类为Inner),然后使用outer.new Inner(),外部类对象无法访问内部类的任何字段。可以在以下地方定义内部类,类、方法(称作局部内部类)、任意的{}作用域,但是不管内部类定义在哪里在编译时会一并被编译成.class文件。匿名内部类如果用到了使用了一个在外部定义的变量,则该变量必须是final的,也就是说必须是常量。内部类也是可以继承的,但是构造方法必须传入外部类对象并且要调用父类对象.super()

    public class PracticeNine extends Inner {PracticeNine(Outer outor) {outor.super();  //由于继承了内部类这里必须这么调用,且构造器必须传入outor对象
    }
    public static void main(String[] args) {PracticeNine practiceNine = new PracticeNine();practiceNine.generatorListener().onClick();
    }
    // 在方法内部定义的内部类
    private OnClickListener generatorListener() {class MyOnClickListener implements OnClickListener {@Overridepublic void onClick() {System.out.println("OnClick");}}return new MyOnClickListener();}
    //在if{}里面定义的内部类
    private OnClickListener generatorListener(boolean b) {if (b) {class MyOnClickListener implements OnClickListener {@Overridepublic void onClick() {System.out.println("OnClick");}}return new MyOnClickListener();} else {return null;}
    }
    class Outer {class Inner {}
    }
    
  22. 嵌套类(静态内部类)创建不需要外部类引用,无法访问外部类的非静态成员(内部可以创建static变量和方法,其实就相当于一个与外部类没关系的类了,普通内部类内部不能创建static方法和字段)

  23. 容器 打印容器不需要循环打印直接println(Collection),容器内不能存储基本数据类型只能存储对象,程序中不应该使用Vector、Hashtable、Stack

    • Collections

      • List(包括ArrayList长随机读取弱插入移除,LinkedList(长插入移除弱随机读取)方法offer()在尾部添加一个元素、peek()返回首个元素如为空则返回null,poll()删除尾部最后一个元素并返回,其实现了队列,并且它也能当做栈使用虽然没实现Stack
      • Set(同一个元素只能出现一次,包括HashSet无序最快查找基于散列表实现、TreeSet升序、LinkedHashSet插入的顺序)
      • Queue(先进先出,主要方法poll(返回并删除第一个元素)与remove()相同、peek(返回第一个元素)与element相同,区别在于空的时候一个返回null一个抛出异常、offer(添加一个元素)),PriorityQueue优先级队列按照比较结果从小到大排列,最先出的就是最小的
      • Stack(后进先出,可以使用LinkedList实现,主要需要实现方法由push()、pop()、peek()、isEmpty())
      • 容器方法:Collections.addAll()、Collections.shuffle()洗牌、Collection.isEmpty()、Collection.clear()、Collection.retainAll()取交集、Collection.toArray()、List.set()替换某个index的值、List.sort()、List.subList()、Arrays.asList()注意该方法生成的会生成一个ArrayList(其内部类)该类并没有重写AbstractList中的删除元素,增加元素的方法(比如add、remove、clear等)调用这些方法将包UnSupportException
    • Map,字典(关联数组),按照key查询值。包括HashMap(HashTable也属于Map)无序最快查找、TreeMap升序(基于红黑树实现)、LinkedHashMap插入的顺序(通过构造器也可以实现LRU,三个参数的)
    • Iterator 迭代器 方法(hasNext()、next()、remove()),当一个类需要能够迭代时可以创建一个迭代器成员变量
    • ListIterator List迭代器,通过listIterator(index)获取index设置锚点起始值,可以做到倒序 比Iterator多的方法(hasPrevious()、Previous()、set()修改当前的值、nextIndex()、previousIndex())
  24. 增强for循环(forEach)可以用于数组(没有实现Iterable)、任何实现了Iterable的类(比如任何Collection) 注:可以自己实现Iterable用于定制,比如生成逆序迭代

    public Iterable<Integer> reverseIterable() {//返回一个逆序的Iterablereturn new Iterable<Integer>() {int index = PracticeThirtyTwo.this.size();@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {@Overridepublic boolean hasNext() {return index > 0;}@Overridepublic Integer next() {return get(--index);}};}};
    }
    
  25. Exception 异常(方法printStackTraces(打印栈的轨迹可以传入PrintStream或者WriteStream)在执行中会调用getMessage(),fillInStackTrace()一般用于捕获了一个异常然后再次将其抛出,如果不调用该方法那么栈的轨迹最终就到创建那个异常的地方,调用了该方法轨迹就到调用该方法的地方,当然也可以捕获一个异常然后抛出另一个异常其栈轨迹将与调用了fillInStackTrace()一样)异常对象一旦创建了堆栈信息就确定了,Logger(使用方法通过Logger.getLogger()获取,然后调用logger.severe()等方法),异常链:Error、Exception的构造器都可以传入一个Throwable,最终打印出来的会包含cause,也就是Android中常出现的上面异常由下面引起(可以捕获异常调用getCause获取异常链上一级),有些现有异常不包含参数为throwable的构造器,可以调用initCause()达到同样的目的,finally会在try catch执行完后执行,如果在try catch中执行了return,会先执行return,但是方法还没结束方法会等到finally执行完后才结束

    • RuntimeException 继承于该类的异常(NullPointException、ArrayIndexOutOfBoundsException等等)属于不受检查的异常,方法不需要声明throws,不进行catch也能够通过编译,一般是JVM抛出,如果调用链全都没处理,那么最后会传到main(),然后把异常传给System.err进行打印
    • 普通异常 属于受检查的异常,如果一个方法抛出一个异常,那么方法必须声明throws,并且外层方法必须catch或者再抛给上层才能通过编译。
    • 异常丢失 两种情况会导致异常丢失,一种是在finally语句中使用return,会丢失前面抛出的未捕获异常,另一种是在子try代码块中抛出一个异常没有catch,并且finally也抛出一个异常,这样在外层catch中仅仅能捕获到finally中抛出的异常,子try代码块中的异常将被忽略
    • 继承/实现带有异常的方法 子类从父类或者接口继承/实现的方法所能抛出的异常只能是接口/父类所抛出的异常的子集,注意构造器则正好相反,父类构造器抛出的异常是子类构造器的子集,如果上转型到父类则必须捕获父类该方法声明的异常,否则就只需要捕获当前类该方法声明的异常
    • 构造器抛出异常(并且当该类不用时需要关闭流) 需要嵌套try catch外层去创建该类的对象,内层去执行(比如I/O)然后执行完后内层finally关闭流,因为当创建对象失败不需要关闭流而进入了内层就需要关闭,所以使用了嵌套try catch,如果构造器不抛出异常则不需要嵌套
    • 子类构造器不能捕获父类构造器抛出的异常,因为子类构造器中没法用try catch包裹super(),super()必须位于第一行
    • 异常匹配 当try代码块中抛出异常后,会寻找最近一个匹配的catch语句然后就认为异常处理结束,所以Exception要放到最后,因为它能匹配(父类可以匹配子类)所有异常
    //代码2处如果不调用fillInStackTrace,那么异常的栈顶信息就指向代码一的位置处否则指向代码二的位置处
    public class TestStackTrace {public static void main(String[] args) {secondThrowException();System.out.println("After Exception");}private static void secondThrowException() {try {firstThrowException();} catch (Exception e) {e.printStackTrace(); //再次抛出的异常的栈轨迹中不包括firstThrowException中的再次throw}}private static void firstThrowException() throws MyException {try {throw new MyException(); //1} catch (Exception e) {System.out.println("firstThrowException获取到异常,再次抛出");throw (MyException) e.fillInStackTrace();//2}}
    }
    class MyException extends Exception {
    }
    
    public class TextExceptionClean {public static void main(String[] args) {Test test1, test2;try {test1 = new Test();try {test2 = new Test();try {//执行一系列操作} finally {test2.clean();test1.clean(); //后创建先清理}} catch (Exception e) {System.out.println("test2 constructor fail");} finally {//关闭test1test1.clean();}} catch (Exception e) {System.out.println("test1 constructor fail"); //不需要关闭}}
    }
    class Test {Test() throws MyException {}void clean() { //创建对象成功才需要清理}
    }
    /* outputs
    matches = false
    firstFind = true Group = 34345 start = 4 end = 8
    SecondFind = true Group = 234 start = 10 end = 12
    ThirdFind = true Group = 123 start = 0 end = 2
    firstLookingAt = true Group = 123 start = 0 end = 2
    secondLookingAt = true Group = 123 start = 0 end = 2
    firstFindWithParams = true Group = 4345 start = 5 end = 8
    firstFindAfterResetWithParams = true Group = 987 start = 0 end = 2 */
    
  26. String对象不可变(只读),任何看起来修改String的方法都只是返回了一个新的String对象,String中的+/+=是java中唯一的两个重载操作符,StringBuilder/SE5新加的(常用方法append、delete、toString)线程不安全,StringBuffer线程安全,不在Loop时由于编译器会自动创建StringBuilder对象然后进行append,所以不用显式创建StringBuilder对象,Loop时必须在Looper外面创建一个StringBuilder,然后在内部Append,如果不显式创建那么每进行一次循环都会创建一个StringBuilder对象影响性能,toString()如果想要获取对象的内存空间可以使用super.toString(),判断两个字符串是否=,java中会有一个独立于堆栈的String池,每次使用一个String如果在池中找不到就会创建,而使用new创建String对象,会在堆中创建一个对象所有使用new创建的和不用new创建的肯定是不相等的

    1. Formatter java中所有的格式化输出都使用该类(%d,%s,%f,%c,%b,%x十六进制,%e科学技术,%h散列码,%7s最小长度为7位的String不足左补空格,%-4s不足右补空格,%04d最小长度4位不足左补零,%4.5f小数保留5位 注意.x不能应用于整数,否则会抛出异常),System.out.printf()、String.format(获取格式化后的字符串)内部也是使用Formatter实现的。
    public class TestFormat {public static void main(String[] args) {int age = 20;float price = 11567.2f;Formatter formatter = new Formatter(System.out);formatter.format("My age is %d and my price is %f", age, price);}
    }
    
    1. Scanner 扫描可以使用nextXXX()获取(也可以在next方法中传入一个正则),默认分割符为空格,也可以使用正则表达式指定
    //使用正则表达式作为分界符
    Scanner scanner = new Scanner("1, 2, 3, 4, 5, 6, 7");
    scanner.useDelimiter("\\s*,\\s*");
    while (scanner.hasNext()) {System.out.println(scanner.nextInt());
    }
    //next使用正则表达式
    String aim = "192.168.2.3\n" +"178.268.33.2\n" +"165.234.123.34";
    Scanner scanner = new Scanner(aim);
    String pattern = "\\d+(\\.\\d+\\.)\\d+\\.\\d+";
    while (scanner.hasNext(pattern)) {System.out.println(scanner.next(pattern));MatchResult matchResult = scanner.match();System.out.println("group0 = " + matchResult.group() + " group1 = " + matchResult.group(1));
    }
    
  27. 正则表达式

    1. ^  输入序列的首个字符
    2. $  输入序列的最后一个字符
    3. \G  前一个匹配的结束??
    4. .  匹配任意除了\r\n以外的所有字符
    5. *  0次或多次匹配前面的字符或者表达式
    6. +  1次或多次匹配前面的字符或者表达式
    7. [A-Z]  匹配所有大写字母
    8. [a-zA-Z]   匹配所有字母
    9. [abc[de]]   等价于[abcde],并集
    10. [a-z&&[abc]]   匹配a或b或c,交集
    11. ?  一个或零个
    12. |  或
    13. [xyz]   匹配里面包含的任一字符
    14. [^xyz]   匹配除了里面包含的任一字符
    15. B   指定字符B
    16. \t 制表符
    17. \n 换行符
    18. \r 回车符
    19. \f 换页符
    20. \d  数字[0-9]
    21. \D  非数字[^0-9]
    22. \W  非词字符等价于[^\w]
    23. \w  词字符[a-zA-Z0-9]
    24. \s 空白符(空格、tab、换行、回车、换页)
    25. \S 非空白符
    26. \b  词的边界
    27. \B  非词的边界
    28. X{n}  恰好n次X
    29. X{n,}  至少n次X
    30. X{n.m}  n<=X出现次数<=m
    31. \xhh 十六进制值为0xhh的字符
    32. \uhhhh 十六进制值为0xhh的unicode字符
    33. \" 匹配双引号因为引号需要转义
    34. 标记模式 前面的常量用于Pattern.compile(第二个参数可以使用|分割以支持多种标记),?!用于正则表达式,效果一致
      1. Pattern.CASE_INSENSITIVE(?!) 匹配忽略字母大小写
      2. Pattern.DOTALL(?x) inputCharSequence中的空格已经以#开头行将被忽略
      3. Pattern.MULTILINE(?m) 在该模式下^、$分别匹配一行的开头和结束,默认两两个分别匹配inputCharSequence的首字符和尾字符
    35. 逻辑操作符 XY: Y字符跟在X字符后面 X|Y: X或Y
    36. 量词 普通贪婪型、勉强型、占有型尾部加+ 包括(?、*、+、{4,}、{4,7})这几种默认是普通贪婪型的即匹配最多字符,如果在尾部加上? 则变成勉强型匹配最少字符,因为上述几种匹配字符数都不确定只是一个范围所以需要有量词
    37. group 正则字符串中每对括号表示一个group(?i、?m不算),groupId为0表示整个表达式,groupId为1表示从左到右遇见的第一个小括号内的内容,Matcher.getGroupCount()获取正则中的所有group数量,默认的group不算
    38. ^|$ 可以匹配第-1位元素和s.length位元素所以“Hello”.replaceAll("^|$", “#”)会变成#Hello#
    39. 小括号的作用如XYZ+表示XY字符加上一个或多个Z,(XYZ)+表示一个或多个XYZ
    40. 将价格格式化为每三个数子带一个,可以使用该正则表达式,这里的?!^不代表忽略大小写,代表的是忽略首位,?=代表的是匹配的首个字符与前一个字符之间
    String regex = "(?!^)(?=(\\d{3})+$)"
    "1234567".replaceAll(regex);
    // 1,234,567
    
    1. Matcher.appendReplacement(替换文本一次,可以指定任意的替换文本,然后把替换后的文本及其前面的文本(到上次替换的文本)写入StringBuffer中)、Matcher.appendTail(把没有替换的后面部分拼接到StringBuffer中)
    public class TestReplacement {                                                                                                                                 private static String aim = "/*! Here's a block of text to use as input to\n" +          " the regular expression matcher. Note that we'll\n" +                           " first extract the block of text by looking for\n" +                            " the special delimiters, then process the\n" +                                  " extracted block. !*/";                                                                                                                                           public static void main(String[] args) {                                                 StringBuffer sb = new StringBuffer();                                                Pattern pattern = Pattern.compile("[aeiou]");                                        Matcher m = pattern.matcher(aim);                                                    while (m.find()) {                                                                   m.appendReplacement(sb, m.group().toUpperCase());                                System.out.println(sb);                                                          }                                                                                    m.appendTail(sb);                                                                    System.out.println("---------------------------------");                             System.out.println(sb);                                                              }
    }
    /* outputs:
    /*! HE
    /*! HErE
    /*! HErE's A
    /*! HErE's A blO
    /*! HErE's A blOck O
    /*! HErE's A blOck Of tE
    /*! HErE's A blOck Of tExt tO
    */
    
    //group相关,行尾指的是\n前
    public class TestGroup2 {static public final String POEM ="Twas brillig, and the slithy toves\n" +"Did gyre and gimble in the wabe.\n" +"All mimsy were the borogoves,\n" +"And the mome raths outgrabe.\n\n" +"Beware the Jabberwock, my son,\n" +"The jaws that bite, the claws that catch.\n" +"Beware the Jubjub bird, and shun\n" +"The frumious Bandersnatch.";public static void main(String[] args) {Matcher m =Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$").matcher(POEM); //意思就是必须包含句尾所以要从后找起相当于最后三个单词while(m.find()) {for(int j = 0; j <= m.groupCount(); j++)print("[" + m.group(j) + "]");println("");}}
    } /* Output:
    [the slithy toves][the][slithy toves][slithy][toves]
    [in the wabe.][in][the wabe.][the][wabe.]
    [were the borogoves,][were][the borogoves,][the][borogoves,]
    [mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.]
    [Jabberwock, my son,][Jabberwock,][my son,][my][son,]
    [claws that catch.][claws][that catch.][that][catch.]
    [bird, and shun][bird,][and shun][and][shun]
    [The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]
    *///:~
    
    //量词
    public class TestMeasureWord {private static String aim = "H000helloWorld7";public static void main(String[] args) {Pattern pattern = Pattern.compile("H\\w+?"); //在+后加了?表示勉强型会匹配最少字符也就是1个所以输出H0,如果不加?则匹配最多字符输出H000Matcher matcher = pattern.matcher(aim);System.out.println(matcher.find() + matcher.group());}
    }
    
    ^[A-Z].*\.$    //匹配一个句子首字符是大写,以句号结尾
    [aeiouAEIOU]   //匹配所有的元音字母
    \\w+ //匹配单词
    \\x61  //匹配a字符// String中有关正则内部都是使用Pattern实现的
    Pattern.matches(String regex, CharSequence input)
    Pattern pattern = Pattern.compile(regex);
    pattern.split(String, ?int) //第二个参数表示分割的字符串数量
    Matcher matcher = pattern.matcher(aim);
    //matches()和lookingAt()都从第一个字符匹配,区别是前者是部分匹配后者是全匹配
    match.matches() //matches:整个匹配,只有整个字符序列完全匹配成功,才返回True,否则返回False。但如果前部分匹配成功,下次匹配的位置将从这后面开始。
    match.lookingAt() //lookingAt:部分匹配,前一个字符能不能匹配,如果不行则前两个字符能不能匹配如果还不行前三个字符能不能匹配依次类推
    matcher.find() //find:部分匹配,从当前位置开始匹配,找到一个匹配的子串,将移动下次匹配的位置。
    matcher.group()
    matcher.start(?int) // 参数groupId
    matcher.end(?int)
    matcher.reset(CharSequence) //无参数表示,把游标重置为字符序列首位,有参数表示更改待匹配字符串
    
    // 测试Matcher的方法,find(?int)、lookingAt()、matches()、reset(?CharSequence)
    public class TestMatcher {private static String charSequence = "123-34345-234-00";private static String regex = "\\d{3,5}"; //[3,5]位数字public static void main(String[] args) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(charSequence);System.out.println("matches = " + matcher.matches()); //没有全部匹配,但是匹配了123,所以游标位于-前面//起始位置变了System.out.println("firstFind = " + matcher.find() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1)); // 找到34335System.out.println("SecondFind = " + matcher.find() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));matcher.reset();System.out.println("ThirdFind = " + matcher.find() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));//lookAt 与find一样部分匹配,但是每次游标都从第一个位置前开始System.out.println("firstLookingAt = " + matcher.lookingAt() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));System.out.println("secondLookingAt = " + matcher.lookingAt() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));//相当于忽略前5个字符,从第五个字符开始匹配,同样会移动游标System.out.println("firstFindWithParams = " + matcher.find(5) + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));//重置游标并且替换待匹配字符串matcher.reset("987-6543-21-0");System.out.println("firstFindAfterResetWithParams = " + matcher.find() + " Group = " + matcher.group() + " start = " + matcher.start() + " end = " + (matcher.end() - 1));}
    }
    /* outputs
    matches = false
    firstFind = true Group = 34345 start = 4 end = 8
    SecondFind = true Group = 234 start = 10 end = 12
    ThirdFind = true Group = 123 start = 0 end = 2
    firstLookingAt = true Group = 123 start = 0 end = 2
    secondLookingAt = true Group = 123 start = 0 end = 2
    firstFindWithParams = true Group = 4345 start = 5 end = 8
    firstFindAfterResetWithParams = true Group = 987 start = 0 end = 2
    */
    
  28. Class 对象拥有以下三种获取方式(假设类名为A,A类对象为a),方法getName()、getCanonicalName()、getSimpleName()、getInterfaces()、isInterface()、getSuperclass()、newInstance(需要有无参构造器,并且类和无参构造器都要是能够访问的)、isInstance(Obj obj)判断obj是否属于这个Class对象代表的类的对象、isAssignableFrom()判断参数Class对象是否继承前者或者实现前者

    1. A.class
    2. Class.forName(A)
    3. a.getClass()
    4. 基本数据类型Class对象可以通过基本类型.class或其包装类型的TYPE字段获取
    5. Class泛型不支持上转型比如这样就不能编译通过Class<Number> integerClass = Integer.class在表示一个Class引用时建议使用Class<?>,虽然其与Class效果一致,如果想要一个Class引用指向一系列继承自某个类的Class对象可以使用Class<? extends Number>表示,这个Class<? extends Number> clazz = int.class;就能编译通过,还有一种getSuperclass().newInstance()只能拿到Class<? super Number>
  29. 动态代理,常用的代理一般是有一个接口和一个已知实现类,然后想要修改那个实现类个某几个方法,然后创建一个类实现接口,在构造器中传入已知类对象,然后把不需要修改方法原样调用已知类对象的对应方法。

public class TestDynamicProxy {public static void main(String[] args) { //调用这个listener里面的任何方法都会走到InvocationHandler的invoke方法OnClickListener listener = (OnClickListener) Proxy.newProxyInstance(TestDynamicProxy.class.getClassLoader(), new Class[]{OnClickListener.class}, new MyInvocationHandler(new MyOnClickListener()));listener.onClick("hfw");}
}interface OnClickListener {void onClick(String name);
}class MyInvocationHandler implements InvocationHandler {private OnClickListener mListener;MyInvocationHandler(OnClickListener mListener) { //把真正需要代理的对象传过来this.mListener = mListener;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("onClick".equals(method.getName())) { //过滤需要代理的对象System.out.println("in handler invoke" + method);}
//        System.out.println(proxy); err死循环,调用proxy对象的toString()又会调用invoke()return method.invoke(mListener, args);}
}class MyOnClickListener implements OnClickListener {@Overridepublic void onClick(String name) {System.out.println("onClick:" + name);}
}
  1. 反射 getXXX与getDeclaredXXX的区别,前者返回本类及其父类的public域,后者返回当前类的所有域,getEnclosingClass获取外部类的Class对象
  2. 泛型 定义了类类型参数,在实例化时要传入对应的类型,不传会报警告,泛型方法则无这个限制,参数化的引用可以赋值给非参数化的引用(反之会抛出警告),泛型在运行时不能new、instanceOf(左边的参数如果为null,则直接返回false),由于不能new那么如何创建泛型对象解决方法之一是在构造器中传入对应的Class对象,然后通过Class.newInstance()创建,在泛型中创建数组使用Array.newInstance(),int[]数组不能赋值或强转成Integer数组反之亦然
    class CreateGenObj<T> {private T mGenObj;void create() {
    //        T genObj = new T(); //不能创建mGenObj = (T) new Object();}T get() {return mGenObj;}public static void main(String[] args) {CreateGenObj<String> genObj = new CreateGenObj<>();genObj.create();// 更加有问题了,Object怎么可能强转成StringString s = genObj.get();}
    }
    class CreateGenArray<T> {private T[] mGenArray;void create() {//创建泛型数组
    //        mGenArray = new T[10]; //不能创建mGenArray = (T[]) new Object[10];}T[] get() {return mGenArray;}public static void main(String[] args) {CreateGenArray<String> cga = new CreateGenArray<>();cga.create();//error Object[] 不能强转成 String[], 原因在于数组内每个元素类型是在创建数组的时候确定下来的//这里创建了一个Object数组,所以每个元素就是Object,由于擦除的原因create方法相当于是//Object[] mGenArray = (Object[]) new Object[10]; 而一旦调用get(编辑器在编译阶段加入了强转成String[]的代码)//这个Object[]数组强转成String[]会报错String[] strings = cga.get();}
    }
    //正确创建步骤
    class CreateGenObjWithRight<T> {private Class<T> mClazz;private T obj;CreateGenObjWithRight(Class<T> clazz) {mClazz = clazz;}void create() {try {obj = mClazz.newInstance();} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();obj = null;}}T get() {return obj;}public static void main(String[] args) {CreateGenObjWithRight<String> genObj = new CreateGenObjWithRight<>(String.class);genObj.create();String s = genObj.get();System.out.println(s);}
    }class CreateGenArrayWithRight<T> {private Class<T> mClazz;private T[] array;CreateGenArrayWithRight(Class<T> clazz) {mClazz = clazz;}void create() {array = (T[]) Array.newInstance(mClazz, 10);}T[] get() {return array;}public static void main(String[] args) {CreateGenArrayWithRight<String> genObj = new CreateGenArrayWithRight<>(String.class);genObj.create();String[] s = genObj.get();}
    }
    
    1. 参数类型,也就是跟在类名后面的<A, B>里面的类型,多个类型使用逗号分割(非静态内部类可以访问外部类的类型参数,静态域不能访问类型参数),基本数据类型无法作为类型参数,可以使用对应的包装类型替换它们,?代表通配符其只能拥有一个边界
    2. 泛型方法
      private static <T> void printClassName(T t) {System.out.println(t.getClass().getName());
      }
      
    3. 泛型数组,把需要使用泛型数组的地方都替换成ArrayList
    4. 显示的类型说明,在.与方法之间加上明确的类型
    5. 元组,有时候方法想要访问一组不同类型的对象就可以创建元组对象,将其返回,使用public final修饰使客户端可以读取值但是不能修改内容,扩展也很容易直接继承就行了
      public class TwoTuple<A, B> {public final A a;public final B b;public TwoTuple(A a, B b) {this.a = a;this.b = b;}
      }
      class ThreeTuple<A, B, C> extends TwoTuple<A, B> {public final C c;public ThreeTuple(A a, B b, C c) {super(a, b);this.c = c;}
      }
      
    6. 擦除 如果声明了泛型编译器会做检查,但是编程成的字节码中并不包含泛型其会被擦除掉,运行时只会保留其原始类型比如List<String>就变成了List只是取值的时候自动做了一步强转(边界,对象进入和离开方法的地方就是发生动作的地方,传入时编译器检查类型传出时编译器自动强转,使用关键字extends定义边界,可以定多个边界使用&分割,由于编译时已经把类型信息擦除,因此在运行时通过Class.getTypeParameters()只能获取到参数标识符和其边界,而无法获取到确切的类型
      ArrayList<String> list = new ArrayList<>();
      list.add("Hello");
      // list.add(123); error
      list.getClass().getMethod("add", Object.class).invoke(list, 2);
      for (String str: list) { //error ClassCastExceptionSystem.out.println(str);
      }
      
    7. 协变与逆变 extends决定上界,super决定下界 在get时使用extends,set时使用super
    8. 原生类型与<?>并不等同,如List与List<?>,前者(相当于List)表示我是一个存储Object的一个List(不需要编译器做额外的检查),什么对象都可以往里面add,后者表示我是某一个类型(使用泛型需要编译器检查)不接受其他的类型,里面除了null什么都不能添加因为编译器无法确定其下界
    9. 捕获转换 当一个容器由一个特定的类型参数转换为原型或者是通配符’?’时,可以通过先用一个使用’?’的方法保存容器,再将容器作为参数传递给泛型方法。例如上面代码中f2调用了f1,这样就可以在泛型方法中使用确切的参数类型
    10. 类型检查,SE5前如果方法接受原生类型比如List(实际传递给它的是List<String>)然后往里面添加了一个元素,这时候并不会报错,但是从List取值并赋值给String时会从报错,如果使用Collections.checkedList()可以在其add一个错误元素时就报错(内部使用代理模式实现)
    11. <T extends A>与<? extends A>的区别,前者只能在类型参数声明的地方进行声明,也就是类名后面或者方法返回值前,后者只能在方法参数中声明
    12. 注意点
       List<Number> list = new ArrayList<Integer>(); //error 泛型不能上转型Class<Number> clazz = new Clazz<Integer>(); //同上List<? extends Number> list = new ArrayList<Integer>(); //合法但是不能调用List中参数涉及参数类型的方法了比如addNumber[] numbers = new Integer[]; //数组这样是合法的但是里面只能存IntegerList<String>[] genericArray = new ArrayList<String>[10]; //error 不能创建泛型数组,一般直接用容器代替
      
  3. 数组
    1. Arrays.deepToString()用来打印多维数组
    2. Arrays.fill(x, y)用y填充数组x中的所有元素,基本数据类型填充值,引用类型填充同样的引用
    3. Arrays.sort(Object[], Comparator)排序要求参数数组对应的元素实现Comparable接口参数二如果设置为Collections.reverseOrder()就表示反序,对String进行排序默认是按A-Z-a-z进行排序的,如果要忽略大小写可以传入第二个参数String.CASE_INSENSITIVE_ORDER
    4. Arrays.binarySearch()二分查找法,要求参数数组必须是有序的,不然会造成有值但是找不到的情况,内部就是利用compareTo就行判断的
    5. 多维数组声明,粗糙数组(每一个维度的元素数量不同 )
      int[][] array = new int[2][] //这样声明一个粗糙数组,一维有两个元素(都是null)
      int[][] array = new int[2][4] //这样声明数组一维两个元素(每个元素是一个长度为4的数组),这样声明的话二维的长度都相同了 array[0].length = array[1].length = 4
      
    6. 数组的类型由其创建时确定,里面只能存储该类型及其子类
      Animal[] animals = new Bird[2];
      animals[0] = new Bird();
      animals[1] = new GHost(); //error ArrayStoreException
      
    7. T[].clone()可以浅拷贝一个数组
  4. 容器进阶 一共4中容器 List、Set、Queue、Map,HashTable、Stack、Vector属于废弃类,不应该使用
    1. Collection.nCopies()可以创建一个List里面每个Item都一样,这个List不能set不能add因为其没重写AbstractList对应的方法,可以将其传入别的容器的构造器或者传入addAll方法。
    2. Collections.fill(),将List中的所有元素都替换成指定元素,如果里面没元素则不进行任何操作
    3. Collection.toArray()、Collection.toArray(T[])前者返回一个Object数组,后者返回一个与传入数组类型一致的数组,如果类型会抛出异常,传入的数组length如果小于size则返回size大小的数组,否则将T[]中空余部分填充为null
    4. Collections.max()、Collections.min()要求也是必须实现Comparable接口或者传入一个Comparator
    5. Arrays.asList() 创建了一个ArrayList(其内部类)内部维护的数组就是外部数组任何会引起底层数据结构的尺寸修改都不允许所以没有重写AbstractList的增删方法比如(add、remove、clear、retainAll)调用这些方法会抛出UnSupportOperationException,但是可以使用该方法生成一个List然后作为Collection的构造器或者addAll方法或Collections.addAll方法参数传入,这样就没限制了,List.subList返回一个SubList内部维护的数组就是外部List的数组
    6. Collections.unmodifiableList()用于创建一个不可更改的List,也就是只读的,调用其他方法将抛出UnSupportOperationException
    7. Set 为了维护set元素的唯一性,会通过各个元素的equal来确保唯一
      1. HashSet 最快的查找速度,判断重复先判断该元素的hashCode是否存在,如不存在,直接插入,如果存在调用equals返回true表示已经存在,否则不存在执行插入
      2. TreeSet 内部原生必须实现Comparable以用来排序(判断重复不使用equals,使用的是compareTo),比较int时不能简单的使用i1-i2,比如i1为21亿,i2为-1亿,i2-i1溢出了返回负值,就导致结果不准确,一般当equal返回了true,compareTo就返回0,否则返回非0,新增方法,comparator()、first()、end()、subSet()、headSet()、tailSet()
      3. LinkedHashSet 内部链表实现,维护顺序
    8. Map的key也是通过equal来判断是否相等的当然散列Map与HashSet一样先根据hashCode判断相等再有equals判断,总之key与Set中的要求是一致的
    9. 可选操作 Collections中的所有读、写操作都是可选的,子类(继承自AbstractXXX的类)可以选择不实现,这样就使用AbstractXXX的默认实现,即抛出一个UnSupportOperationExceptions
    10. ListIterator 初始游标位于0最大位于size(),假设游标位于1,remove删除元素1,add在元素1-2之间插入,set设置元素1的值,其中remove、set必须紧跟next,使用Iterator和ListIterator注意在迭代时不要去操作原来的Collection/List,不然如果导致内外ModCount不同就会抛出异常
    11. Collections.rotate() 所有元素向后移动N位,最后的移动到第一位
    12. Collections.swap(List) 速度比较快
    13. 通过Collections.synchronizedXXX()来转化为线程安全的容器
    14. Collections.disjoint(v1,v2) 如果v1和v2没有任何相同元素返回true
  5. 注意点
    1. 写代码的时候必须要先考虑能不能使用已有的代码写代码的时候必须要先考虑能不能使用已有的代码
    2. 声明一个内部类引用必须声明成Outer.Inner(在main方法中创建一个非静态内部类使用outer.new Inner(),赋值给Outer.Inner注意不能直接赋值给Inner)
    3. 创建链表的时候要先创建一个空Header,即内容和next都为null的Header
    4. char都是正数,如果赋值为-1,实际上表示的是65535,因为char是无符号的
  6. 文件
    1. Path getPath获取的是相对路径也就是短路径,getAbsolute获取的绝对路径也就是长路径
    2. Read、Write canRead、canWrite返回是否可以读写
    3. renameTo可以用来修改文件名也可以用来移动文件比如剪切
    4. mkdir创建一层路径,如果目标路径的parent路径不存在则失败,mkdirs创建n层路径,如果parent路径不存在则创建,调用了这两个方法那么该File对象就代表了一个路径
      1. 4个基类(抽象类) InputStream、OutputStream、Reader、Writer,前两个操作的是字节,后两个操作的是字符
      2. 4个装饰流基类 FilterInputStream、FilterOutputStream、FilterReader、FilterWriter
      3. InputStream、OutputStream转化为Reader、Writer通过InputStreamReader和OutputStreamWriter转化
      4. 默认情况下FileOutputStream写入一个已经存在的文件会先把文件清空然后再写入,可以通过给构造器传入第二个参数表明是否采用append方式
      5. 路径问题 比如要读取com.hfw.test.A可以使用./src/com/hfw/test/A或者src/com/hfw/test/A
      6. DataInputStream、DataOutputStream读取\写入byte、long等基本数据类型,可以将DataOutputStream写入的文件通过DataInputStream复原,基本可以使用RandomAccessFile(操作对象也是字节)来代替这两个类
        File file = new File("data/data.txt");
        if (file.exists()) {System.out.println(file.delete() ? "删除成功" : "删除失败");}System.out.println(file.getParentFile().mkdir() ? "创建父路径成功" : "创建父路径失败");System.out.println(file.createNewFile() ? "创建文件成功" : "创建文件失败");DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));dos.writeBoolean(true);dos.writeInt(24);dos.writeUTF("1995.12.15");dos.close();DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));System.out.println(dis.readBoolean() ? "男" : "女");System.out.println("age is " + dis.readInt());System.out.println("birthday is " + dis.readUTF());dis.close();
        
      7. BufferedReader 能够readLine()并且能提高读取能力,BufferedWriter 能够加快写速度,注意文件不存在时不能写,需要先调用createNewFile创建一个文件,写完文件记得flush一下,close流有flush的功能
      8. 文件输出,只要保证目标文件的父路径存在就行了,如果不存在会抛出FileNotFindException,不需要createNewFile
      9. 重定向 标准输入输出,比如将其输出到文件中可以使用System.setOut
      10. 调用命令,使用ProcessBuilder.start()
      11. nio, 主要分为Channel、ByteBuffer,我没从缓存区中存数据或者是取数据,ByteBuffer.wrap() 可以根据参数创建一个缓冲器,ByteBuffer.allocate用于创建指定大小的缓冲器。
        FileChannel fc1 = new FileInputStream(getFilePath(CopyFileWithNio.class)).getChannel();
        FileChannel fc2 = new FileOutputStream("data/CopyFileWithNio.java").getChannel();
        fc1.transferTo(0, fc1.size(), fc2);
        
      12. ByteBuffer内部主要是position、limit、capacity、mark,方法 flip: limit = position,position = 0 一般用于准备从缓冲区读取已经写入的内容、clear: limit = capacity,position = 0、
        rewind: position = 0、mark: mark = position、hasRemaining,position与limit之间是否有元素、remaining:返回limit - position,mark主要用来跟reset配合,reset可以把position置为mark,position在调用无参的get还有单参数的put时position会发生变化,调用带索引的get与put不会改变position
      13. 文件锁通过fc.tryLock()、fc.lock()获得文件锁,某些操作系统的文件一旦被某一个进行获得了文件锁其他进程就不能写该文件,也有一些操作系统文件被获取的文件锁,其他进程照样可以写该文件(只是获取不到锁)这样会导致多进程写文件,最终导致文件损坏(比如Mac),所以在写文件时最好都获取一下锁,如果锁获取不到表明有其他进程正在写该文件。
      14. 对象序列化,Serializable在开发艺术之旅中已经有了,Externalizable可以做到部分序列化,要求写入Object和读取Object都由自己控制,并且在反序列化的时候会先调用默认构造器然后再调用readExternal,transient关键字修饰的字段可以阻止序列化。Serializable类也可以加上readObject和writeObject方法用与自己决定保存哪些对象又复原哪些对象注意这两方法必须是private的,defaultReadObject / defaultWriteObject可以读/写所有非transient字段,transient字段必须在这两个方法中明确指定,此外对象序列化也能够实现对象的深拷贝可以字节数组输入、字节数组输出流做到,静态字段不能序列化,如果需要序列化需要自己手动保存字段,然后再读出
        class Data implements Serializable {public transient String name;public int age;public Data() {System.out.println("Default constructor");}public Data(String name, int age) {this.name = name;this.age = age;}private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {// 读取所有非transient字段,defaultReadObject必须是第一条读取System.out.println("ReadObject");ois.defaultReadObject();name = (String) ois.readObject();}private void writeObject(ObjectOutputStream oos) throws IOException {// 写入所有非transient字段,defaultReadObject必须是第一条写入System.out.println("WriteObject");oos.defaultWriteObject();oos.writeObject(name);}}
        
      15. java中也有Preferences可以使用Preferences.userNodeForPackage获取,里面的内容会进行持久化不随着退出java进程而消失,Android里面不行进程退出保存的数据就没了
  7. 枚举
    1. Enum.valueOf可以根据字符串获取枚举对象
    2. Enum实现了comparable接口,可以调用compareTo
    3. getDeclaringClass 获取是那个枚举类对象
    4. 当枚举类中没有其他属性和方法可以省略枚举最后的一分号
    5. switch使用枚举其实使用的是枚举的ordinal属性进行比对的
    6. Class.getEnumConstants 可以获取该类中的所有枚举对象
    7. 从反编译结果可以看出,编译器会自动创建一个继承于Enum的子类,不允许被继承因为是final的,编译器会自动加上values和valueOf两个方法
    8. EnumSet简单来说就是通过EnumSet.noneOf创建空Set或者通过of创建非空数组或者allof创建一个全枚举的Set其他跟普通的Set没什么两样,原理就是里面保存了一个long数组如果枚举数量小于64举例某个枚举对象A其ordinal为38则long从右到左的第38个bit位为1,其他位是0
    9. EnumMap速度也很快,内部就是一个Object数组,初始化时就创建了一个与枚举对象等长度的数组,然后以ordinal为下标进行put,get,
    10. Enum类中可以定义抽象方法,其所有枚举实例都得实现该方法,相当于创建了一个匿名内部类
      public final class com.hfw.test.Enum1 extends java.lang.Enum<com.hfw.test.Enum1> {public static final com.hfw.test.Enum1 AAAA;
      public static final com.hfw.test.Enum1 C;
      public static final com.hfw.test.Enum1 D;
      public static final com.hfw.test.Enum1 E;
      public static com.hfw.test.Enum1[] values();
      public static com.hfw.test.Enum1 valueOf(java.lang.String);
      static {};
      }
      enum TestE {AA{@Overridevoid command() {}@Overridevoid secondCommand() {super.secondCommand();}};abstract void command();void secondCommand() { }
      }
      
  8. 注解 元注解专门用来注解其他注解,使用注解必须给出所有没有默认值的键 = 值,value除外,可以直接给出值
  • @Target 表明什么地方可以使用该注解,定义在ElementType,多个可以用逗号分割,全部区域则可以直接省略@Target
    1. CONSTRUCTOR:用于描述构造器
    2. FIELD:用于描述域
    3. LOCAL_VARIABLE:用于描述局部变量
    4. METHOD:用于描述方法
    5. PACKAGE:用于描述包
    6. PARAMETER:用于描述参数
    7. TYPE:用于描述类、接口(包括注解类型) 或enum声明

  • @Retention表示需要在什么级别保存该注解信息,定义在RetentionPolicy
    1. SOURCE: 注解将被编译器忽略
    2. CLASS: 注解在class文件中可用,但会被VM抛弃
    3. RUNTIME: 注解在运行时也保留,所以可以通过反射读取注解的内容

  • @Documented 将此注解包括在javadoc中

  • @Inherited 运行子类继承父类的注解

  • 注解中所允许的类型包括基本数据类型、String、Enum、Class、Annotation、这几个的数组,不能使用包装类型

      // 基本注解,Test里面没元素,属于标记注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interface Test {}// 使用时必须要指定id,desc由于有默认值可以不指定@Test(id = 4, desc="hfw")@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interface Test {int id();String desc() default "no description"; // 定义默认值,如果没有默认值那么在使用该注解时必须提供该值,并且默认值不能为null,注解时提供的值也不能为null}Method[] methods = AnnotationOne.class.getDeclaredMethods();for (Method method : methods) {Test anno = method.getAnnotation(Test.class);if (anno != null) {System.out.println("methodName = " + method.getName() + " id = " + anno.id() + " desc = " + anno.desc());}}```
    
  1. 并行与并发 并发一个时间片执行任务A,下个时间片执行任务B。并发同一时间片同时执行任务A和任务B
  2. 线程
    1. 一旦线程开启了在其run方法没执行完前垃圾回收器是不能回收的
    2. Thread.yield 表明当然线程暂时不需要CPU可以让给其他线程,不保证一定会让出CPU,只是建议可以把CPU让给其他相同优先级的线程,不会让出锁(sleep方法也一样不会让出锁),wait方法会让出锁(与notify和notifyAll一样只能在同步方法和同步代码块中调用,不然运行时会抛出异常)
    3. ExecutorService 不用了要记得shutdown不然会等到里面的所有线程都死了才会关闭,构造器同时接受一个ThreadFactory对象,可以用来设置优先级,Daemon,名称等
    4. SingleThreadExecutor 等于FixedThreadExecutor(1),里面加入的所有任务会按照顺序执行(感觉里面维护了一个队列)
    5. ExecutorService可以调用submit将一个Callable对象传入会返回一个Future对象对其调用get()会阻塞获取运行结果,isdone用于获取是否已经完成,android中想不到那里有用,主线程也不能傻傻等着
    6. 父线程无法捕获子线程抛出的异常,子线程抛出了异常没捕获不会影响到父线程(!!!Android是个例外如果子线程抛出了一个未捕获的RuntimeException会导致程序Crash,因为这个了异常处理器,发现任何线程抛出异常都会强行终止app)
    7. 可以对线程对象调用thread.setUncaughtExceptionHandler,当该线程跑出来一个未捕获的异常时会回调该方法,也可以调用Thread类的静态方法Thread.setDefaultUncaughtExceptionHandler,当thread没有调用setUncaughtExceptionHandler时会调用默认的DefaultUncaughtExceptionHandler,如果设置了就不调用,就近原则(有具体的调用具体,没有具体的调用默认的)
    8. volatile确保不会不进行任何编译器优化
    9. 后台线程在还有非后台线程在运行的情况下可以一直运行,当没有非后台线程在运行了会自动退出,只要在调用Thread.start之前调用setDaemon,一个后台线程启动的所有线程都是后台线程,并且在非后台线程全都停止运行了以后会立刻关闭所有的后台线程,就算后台线程有finally语句也不会得到执行
    10. 实现Runnable与继承Thread的区别,前者还可以继承其他类,后者不能再继承了
    11. t1.join()表示当前线程需要等待t1线程执行完后才能继续运行,也可以传入一个timeout,表示最多等待多时毫秒
    12. 当线程thread1进行sleep,可以调用thread1.interrupt进行打断,但是当thread1线程该异常被捕获后thread1.isInterrupt会变成false
    13. 自增自减操作不是原子的,包括两条语句先把变量+1/-1然后再赋值给变量
    14. wait、notify、notifyAll使用方法
      1. 三个方法都必须在同步代码块/方法中调用
      2. 如果要唤醒处于wait的线程首先必须获得wait方法所锁住的对象锁,然后调用该对象的notify方法(调用该方法不会立即释放锁,而会等到所在的同步代码块执行完后才会释放锁)
      3. notify只会随机唤醒一个处于wait的线程,notify会唤醒所有处于等待的线程(并且线程间公平竞争锁)
    15. synchronized、volatile
      1. 如果一个非静态方法被声明为synchronized那么对于一个对象来说如果一个线程调用了该方法,那么在该方法返回前,这个对象的其他的synchronized方法将不能被调用(获得锁的那个方法可以调用其他synchronized方法),相当于非静态方法加同步锁,锁的是对象

        TestSynchronized t = new TestSynchronized();
        new Thread(() -> t.A()).start(); //导致其他线程不能调用t.C
        try {TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        t.C(); //阻塞,获取不到锁class TestSynchronized {public synchronized void A() {while (true) {// 什么都不干B(); //进入这里表示当前线程已经获得了锁,所以可以调用B()}}public synchronized void B() {System.out.println("B");}public synchronized void C() {System.out.println("C");}
        }
        
      2. 如果一个静态方法被声明为synchronized那么,当一个线程调用该方法,在该方法没返回之前,其他线程无论是通过对象调用该类中的synchronized方法还是通过类名调用,都会被阻塞,静态syncronized方法其实锁的是类的字节码对象
      3. 可以显示的创建一个锁。以用来替换synchronized关键字(可以调用tryLock来尝试获得锁,如果获取不到锁可以先去干什么事)Lock lock = new ReentrantLock();
        public int add() {lock.lock();try {++age;++age;++age;return age;} finally {lock.unlock();}}
        
      4. 原子性 原子操作表示一旦操作开始那么在操作结束之前,是不会进行上下文切换的,读取、写入除long、double以外的基本数据类型都可以视为是原子操作,long、double因为JVM可以将其当做两个32位操作来执行所以不是原子操作,如果long、double被定义为volatile那么也是原子操作,因为JVM不能将其拆成两个32位操作了
      5. 可见性 关键字volatile提供了可见性(为了解决多线程的易变性)以及禁止指令重排序,如果一个域不声明为volatile,在每个线程就会都存在一个该域的副本一个线程改了该域只是改了其副本的内容,在其写回主存前其它线程可能感觉不到该域已经发生了改变,通过volatile关键字修饰,修改了该域会立即写回主存,这样就不会导致其他线程不可见,如果一个域完全由同步方法与语句保护,就不需要设置成volatile,因为同步也会导致向主存中刷新。注:成员变量不属于任何一个线程因此主线程也存在成员变量的一个备份,所以以下代码有两个问题1. getValue是个原子操作但是evenincrement不是一个原子操作所以可能只自增了一次就被getValue获取到了导致出现奇数2.因为i没有被volatile修饰所以可见性也会是问题,子线程更改了i主线程可能看不到。解决办法是将getValue变为同步方法i变为volatile,其中如果不加volatile虽然可见性问题依然存在,但是程序不会出现奇数。因为调用getValue如果发现拿不到锁也就是evenincrement没有执行完会阻塞
        public class AtomicityTest implements Runnable {private int i = 0;public int getValue() {return i;}private synchronized void evenIncrement() {i++;i++;}public void run() {while (true)evenIncrement();}public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();AtomicityTest at = new AtomicityTest();exec.execute(at);while (true) {// getValue的时候可能evenIncrement处于中间态导致只++了一次,解决方法是getValue也加上同步,在evenIncrement方法没有执行好,调用getValue将阻塞int val = at.getValue();if (val % 2 != 0) {System.out.println(val);System.exit(0);}}}
        }
        
      6. synchronized代码块需要一个对象充当锁(一般是正在调用该方法的对象this,如果需要同步多个对象的同一个方法那么可以创建一个Object对象作为成员变量,或者直接使用类的字节码),只有当一个线程拥有该对象的锁才能执行同步代码块
  3. 线程的状态
    1. 新建(new) 线程被创建时,这个状态只会维持很短的一段时间。此时线程已经拥有获得CPU时间的资格了,之后调度器将把这个线程转化为就绪状态或者阻塞状态
    2. 就绪(runnable) 在这种状态下只要调度器把时间片分给该线程,该线程就能运行,包括现在获得时间片正在运行的线程
    3. 阻塞 线程可以运行,但是由于某个条件阻止其运行,当线程处于该状态,调度器将忽略该线程,直到线程重新进入就绪状态,才有可能执行操作
    4. 死亡 当run方法执行完后线程就属于死亡状态该状态的线程是不能被调度的,是不会得到CPU时间的
  4. 进入阻塞状态的几大原因
    1. 调用Thread.sleep(),任务在指定时间内不会得到运行
    2. 调用wait(),直到线程得到了notify、notifyAll、signal、signalAll的消息线程才会进入就绪状态
    3. 线程在等待某个输入/输出完成
    4. 任务试图调用某个同步代码块或者同步方法,而又得不到锁,会进行阻塞
  5. 关闭线程 1. stop不推荐 2. thread.interrupt(不能关闭处于IO阻塞(可以通过关闭阻塞的流)、同步代码块阻塞,可以关闭Thread.sleep)
  6. 可以使用javap -c 类名来反编译.class文件
  7. 为什么要重写hashCode,当重写了equals也就是想用内容判断是否相等,当A.equals(B)时,但是它们的hashCode不同,这样会导致在使用散列容器中被认为两个对象不相等

Thinging in Java读后总结相关推荐

  1. 深入java虚拟机需要读吗_《深入理解Java虚拟机》读后总结(一)JVM内存模型

    <深入理解Java虚拟机>读后总结 基于Sun HotSpot JVM 直接上图: 从图中看到,JVM内存分为两个主要区域,一个是所有线程共享的数据区,一个是线程隔离数据区(线程私有) 线 ...

  2. 吐槽java之《程序员的呐喊》读后总结

    <程序员的呐喊>读后总结 --关于java的批判 一.写在总结前面的一些废话 <程序员的呐喊>(后文简称呐喊),是一本非常有趣的散篇,全文都是作者对目前软件开发界的看法,主要翻 ...

  3. Java读带有BOM的UTF-8文件乱码原因及解决方法(转)

    转载:http://www.linuxidc.com/Linux/2012-12/76707.htm 最近在处理文件时发现了同样类型的文件使用的编码可能是不同的.所以想将文件的格式统一一下(因为UTF ...

  4. Java读取UTF-8格式txt文件第一行出现乱码及解决;Java读带有BOM的UTF-8文件乱码原因及解决方法(转载)...

    原文地址:http://blog.csdn.net/jackpk/article/details/5702964/ Java读取UTF-8的txt文件第一行出现乱码"?"及解决 t ...

  5. JAVA读文件类之FileReader/InputStreamReader/BufferedReader

    上一篇写了Java写入文件操作,这一篇回顾一下Java读文件操作. Java IO采用reader类来进行文件读取,而且已经提供了三个Reader的实现类,FileReader,InputStream ...

  6. Android导航语音识别——读后听写语音识别(嵌入式)

    嵌入式项目之Android语音识别--读后听写语音识别(语音识别功能主函数调用) 文章目录 前言 一.代码示例 二.代码分析 1.创建 SpeechSynthesizer 对象 2.设置合成参数 3. ...

  7. Java读带有BOM的UTF-8文件乱码原因及解决方法

    Java读带有BOM的UTF-8文件乱码原因及解决方法 Java读带有BOM的UTF-8文件乱码原因及解决方法 - daimojingdeyu - ITeye技术网站 Java读带有BOM的UTF-8 ...

  8. 新生 语不惊人死不休 —— 《无限恐怖》读后有感

    开篇声明,我博客中"小心情"这一系列,全都是日记啊随笔啊什么乱七八糟的.如果一不小心点进来了,不妨直接关掉.我自己曾经写过一段时间的日记,常常翻看,毫无疑问我的文笔是很差的,而且心 ...

  9. 参加java培训后,就业方向有哪些

    参加java培训后,就业方向有哪些?很多正在参加java培训的同学对这个问题都比较关注,那么下面小编就针对这个问题为大家做下详细的介绍,希望能够帮助到大家. 参加java培训后,就业方向有哪些?首先我 ...

最新文章

  1. 张亚勤2020寄语哥伦比亚大学毕业生:引领未知时代
  2. 怎么打开外部文件_保存的DWG文件再次用CAD打开时提示文件损坏了怎么办?【AutoCAD教程】...
  3. qlineedit文本改变时_行文本编辑框QLineEdit及自动补全
  4. Cisco路由器配置命令之模式转换命令
  5. MySQL 高级 - 启动及登录MySQL
  6. 拼接字符串去掉最后多余的串,JSON的遍历
  7. css二级菜单会然下面遮住_JavaScript实现下拉二级菜单详解
  8. 通过GitHub Pages创建个人主页
  9. 17pk扎金花基于层次的技术
  10. shell脚本中的逻辑判断,文件目录属性判断,if特殊用法,case判断
  11. getprivateprofilestring读不到数据_SpringBoot2.x系列教程66--Spring Boot整合分布式事务之数据库事务回顾
  12. Asp.net MVC4 下二级联动
  13. A1028[List Sorting] 小水题
  14. 搬运视频抖音封号md5视频修改工具
  15. ADA本月上涨100%,背后或有三个原因
  16. 国际商业分析师CBAP认证与PMI-PBA认证的区别——上海信息化培训中心
  17. capturing self strongly in this block is likely to lead to a retain cycle 警告解决
  18. 查询自己名下所有微信账户
  19. 关于网络游戏的影响(腾讯游戏)
  20. 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权

热门文章

  1. NSACE认证|从事网络安全行业需要哪些知识储备?
  2. IEEE802.16e 协议中LDPC编解码原理说明
  3. ios设备使用socks代理_iOS 使用socks5代理服务器
  4. django 进阶第二天 生鲜超市学习 model
  5. 生鲜超市 学习进阶第三天 xadmin的后台管理
  6. python运行界面如何缩小_如何使用Python调整图像大小
  7. Spark 全套知识体系,终于搞到了!
  8. [BZOJ]4180: 字符串计数 SAM+矩阵乘法+二分
  9. R语言使用ggplot2可视化甜甜圈图(Donut chart)
  10. ORACLE_CMD命令(最全的)