JDK-JRE-JVM概述 
JRE (Java Runtime Environment): Java运行环境,如果要运行Java程序,就需要JRE的支持,JRE里包含JVM。
JDK (Java Development Kit): Java开发工具,包含java程序的所有工具,javac和java,JDK里包含JRE
JVM (Java Virtual Machine) 简称JVM,它是运行所有Java程序的虚拟计算机,好比模拟器,JNM是Java语言的运行环境。JVM用于读取并处理编译过的与平台无关的字节码,从而实现Java的可移植性。选择不同平台的JDK。
JVM是Java程序的解释和执行器。
Java JDK的使用 
bin 存放Java的操作工具,比如编译工具javac等
include:存储c++的头文件
jre:java的运行环境,内有JVM
lib:java运行和核心库
src.zip:java的源代码
1 2 3 4 5 $ javac 123 .java $ java hello 
java基本语法 
java语言严格区分大小写,好比main和Main是完全不同的概念 
一个java源文件里可以有多个java类,其中最多有一个类被定义为pulic,若源文件中包括了pulic类,源文件必须和pulic类同名 
一个源文件中包涵N个java类时,编译后会生成N份字节码文件,即每个类都会生成一份单独的class文件,且字节码文件名和对应的类名相同 
一个类必须有main方法才能运行,main方法是程序的入口。 
 
建议:
 
JVM内存模型 
JVM内存划分,认为的根据不同内存空间的存储特点以及存储的数据。
程序计数器:当前线程所执行的字节码的行号指示器。 
本地方法栈:为虚拟机使用的native方法服务。 
方法区:线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量即时编译器编译后的代码数据等(这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载) 
java虚拟机栈:简称栈,每个方法被执行的时候都会同时创建一个栈帧用于存储改方法的局部变量、操作栈、动态链接、方法出口灯信息。
每当调用一个方法时,创建一个栈帧,存放了当前方法的局部变量,当方法调用完毕,改方法的栈帧就被销毁了。 
 
 
java堆:被所有线程共享的一块内存区域,在虚拟机启动时创建,所有的对象实例以及数组都要在堆上分配。
每次使用new关键字,就表示在堆内存中开辟一块新的内存空间。 
 
 
 
GC(Garbage Collection),垃圾回收器。
javaBean 栈帧:C语言中,每个栈帧对应一个未运行完的函数,栈帧中保存了该函数的返回地址和局部变量。
从逻辑上讲,栈帧就是一个函数执行的环境,函数参数,函数的局部变量,函数执行完后会返回到哪里等。
类 
同一个包 
子类 
任何地方 
 
 
private 
✅ 
 
无 
✅ 
✅ 
 
protected 
✅ 
✅ 
✅ 
 
public 
✅ 
✅ 
✅ 
✅ 
 
JavaBean规范:
类必须使用public修饰 
必须保证有公共无参数的构造器,即使手动提供带参数的构造器,也得手动提供无参数构造器 
字段使用private修饰,每个字段提供一堆getter和setter方法 
 
每个字段都得使用private修饰,并提供一堆getter/setter方法
 
继承思想 
子类可以继承父类的全部成员(成员变量和方法),但private和无修饰的字段没有访问权限
方法覆盖 
方法签名相同(方法签名 = 方法名 + 方法参数) 
方法返回类型必须相同 
子类方法中抛出的异常类型应该小于等于父类方法中抛出的异常类型 
子类方法的访问权限必须大于父类方法的访问权限 
 
只有实例方法(无Static修饰)才称之为方法覆盖
 
方法重载 
同一个类中,方法名相同 
方法参数列表不同 
 
注意: 
方法覆盖和重载的区别:
 
抽象 使用abstract修饰的方法,称为抽象方法
1 public  abstract  返回类型 方法名(参数);
特点
使用abstract修饰,没有方法体,留给子类去覆盖 
抽象方法必须定义在抽象类或者接口中 
使用abstract修饰的类称为抽象类。 
 
 
1 public  abstract  class  类名
抽象类不能创建对象,调用没有方法体的抽象方法没有意义 
抽象类中可以同时拥有抽象方法和普通方法 
抽象类要有子类才有意义,子类必须覆盖父类的抽象方法,否则子类也得作为抽象类。 
 
Object == 和 equals的区别
== 比较基本数据类型,比较值是否相等,比较应用类型时,比较内存空间是否相等
equals只能比较对象	
覆盖equals  比较方式就看equals的方法体,一般比较内容是否相等 
不覆盖equals  使用Object的equals方法比较,实际上就是用==比较两个对象是否相同,比较内存空间 
 
接口 接口是一种规范,只定义应该有哪些功能,本身不实现功能。
接口定义 1 2 3 4 5 public  interface  接口名                } 
接口表示具有某些功能的事物,接口名使用名词,有人习惯I打头,IWalkable.java
1 2 3 4 5 public  interface  IWalkable      void  walk ()           public  abstract  void  walk ()  } 
接口实现类 类和接口之间的关系是implements,实现关系
1 2 3 public  class  类名 implements  接口名      } 
如果实现类没有全部实现接口中的方法,要么报错,要么把实现类设置为抽象类。 
多态 编译类型和运行类型不一致 Animal a = new Cat();
编译类型必须是运行类型的父类或接口
父类引用子类变量指向子类对象,调用方法时,实际调用的是子类方法 
多态时方法调用问题 
1 2 Animal a = new  Cat(); a.shout(); 
上述代码的逻辑图
类型转换和instanceof运算符 instanceof
语法格式:boolean b = 对象A  instanceof 类B; // 判断A对象是否是B类的实例。如果是返回true 
 
面向接口编程,体现的就是多态,好处是:把实现类对象赋给接口类型变量,屏蔽了不同实现类之间的实现差异,从而可以做到通用编程。
this and super and static super用法同this
在方法中,哪一个对象调用this所在的方法,此时this就表示哪一个对象。
this在构造器中的用法需要注意的是:
一般少参构造器调用多参构造器,this调用构造器必须为当前构造器方法中的第一行代码
 
static static 修饰的成员变量,随着类被加载进JVM,内存储存在方法区中。
使用对象访问static修饰的成员变量,底层依然使用类名访问。 
static方法访问的成员变量必须使用static修饰,必须依托类,而不是对象。
在类中,代码块是由上到下依次运行的,但是由static修饰的代码块,会在类加载到内存中就运行依次,之后如果不调用则不再运行。
 
final final可以修饰类、方法、变量
final修饰类时,表示该类不能再有子类 
final修饰方法时,表示该方法不能被子类所覆盖 
final修饰变量,表示常量,改变量只能赋值一次,不能再重新赋值。
基本数据类型:表示值不能被改变 
引用数据类型,所引用的地址不能被改变(即该变量指针不能重新指向其他地址); 
 
 
 
内部类和匿名内部类 内部类可以看做和字段、方法一样,是外部类的成员变量,也可以被static修饰。
静态内部类 静态内部类:使用static修饰的内部类,访问内部类直接用类名.静态类构造器创建静态类
静态的内部类中,不能访问外部类中非静态的变量和方法 
使用场景,一般针对于,静态内部类不需要外部类的资源,而外部类需要使用内部类的时候。
静态类的调用
1 2 Cat.Dog d = new  Cat.Dog(); d.fun(); 
实例内部类 实例内部类:访问内部类,使用外部类实例的对象来访问。
匿名内部类 针对类,定义匿名内部类来继承父类(使用较少)
1 2 3 new  [父类构造器]([实参列表]) {     } 
针对接口,使用较多
1 2 3 4 5 6 7 8 9 10 new  接口名称() {     } board.plugin(new  IUSB() {     public  void  swapData ()           System.out.println("打印" );     }  }); 
匿名类的底层,依然还是创建了一份字节码文件,USBDemo$1,其反编译为:
1 2 3 4 5 6 class  USBDemo $1 implements  IUSB     public  void  swapData ()           System.out.println("打印" );     } } 
枚举 枚举类的由来: 1 2 3 4 5 6 7 public  class  Type      public  static  final  Type MAN = new  Type();     public  static  final  Type WOMAN = new  Type();     public  static  final  Type NETURAL = new  Type();          private  Type ()  } 
枚举类的定义和使用 public enum 枚举类名 {
我们自定义的枚举类在底层都是直接继承了java.lang.Enum类的。
1 2 3 4 5 6 7 public  enum  Type {    MEN, WOMEN, NEUTRAL; } Type t = new  Type.MEN; t.name();  t.ordinal();  
单例模式 目的:保证在整个应用中某一个类有且只有一个实例,(一个类在堆内存中只有一个对象)。
步骤:
必须在该类中,自己先创建一个对象。 
私有化自身构造器,防止外界通过构造器创建新的工具类对象 
向外暴露一个公共的静态方法用于返回自身的对象。 
 
1 2 3 4 5 6 7 8 public  class  ArrayUtil      private  static  ArrayUtil instance = new  ArrayUtil();     private  ArrayUtil ()       public  static  ArrayUtil getInstance ()           return  instance;     }      } 
基本类型的包装类 除了Integer和Character外,其他都是基本类型的首字母大写
装箱和拆箱
把基本类型数据转成对应的包装类对象。反之是拆箱
1 2 3 4 5 Integer num2 = Integer.valueOf(17 ); Integer num2 = 17 ;   int  val = num2.intValue();int  val = num2;     Object obj = 17  
把字符串转换为int,int num = Integer.parseInt(“123”); 
如果传入的不是纯数字字符串会抛出异常”NumberFormatException”
六种转换操作:
Integer -> int int val = num2.intValue(); int val = num2; 
int -> Integer Integer num2 = Integer.valueOf(17); Integer num2 = 17;
Integer -> String num2 + "" num2.toString()
String -> Integer Integer.valueOf(String s)
int -> String num3 + ""
String -> int int num = Integer.parseInt("123");
 
缓存设计 Byte、Short、Integer、Long缓存范围[-128, 127];
1 2 3 4 5 6 7 8 9 Integer i1 = new  Integer(123 ); Integer i2 = new  Integer(123 ); System.out.println(i1 == i2);  Integer i1 = Integer.valueOf(123 ); Integer i2 = Integer.valueOf(123 ); System.out.println(i1 == i2);  
部分源码如下:
1 2 3 4 5 public  static  Integer valueOf (int  i)      if  (i >= IntegerCache.low && i <= IntegerCache.high)         return  IntegerCache.cache[i + (-IntegerCache.low)];     return  new  Integer(i); } 
BigDecimal 处理类似精确2位这样的需求
1 2 3 System.out.println(0.01  + 0.09 ); System.out.println((new  BigDecimal(0.01 )).add(new  BigDecimal(0.09 ))); System.out.println(new  BigDecimal("0.01" ).add(new  BigDecimal("0.09" ))); 
随机数 Random Math.random(); // 返回一个[0,1)的随机小数
Random类用于产生一个伪随机数(通过相同的种子,产生的随机数是相同的),Math类的random方法底层使用的就是Random类的方式。
UUID UUID表示通用唯一标识符(Universally Unique Identifier),其算法通过电脑网卡、当地时间、随机数等组合而成,优点是真实的唯一性、确定是字符串太长。
1 2 3 4 5 String uuid = UUID.randomUUID().toString(); String code = uuid.substring(0 , 5 ); code.toUpperCase(); 
字符串String类 字符串=字符序列,
不可变字符串:String str = “ABCD”; 等价于 char[] cs = new char[]{‘A’, ‘B’, ‘C’, ‘D’};对象内容改变,则变成了一个新的对象。 
可变字符串 StringBuilder/StringBuffer:当StringBuilder对象创建完毕后,对象的内容可以发生改变,当内容发成改变的时候,对象保持不变。 
 
String 对象创建的两种方式,有什么区别:
1 2 1 . String str1 = "ABCD" ;2 . String str2 = new  String("ABCD" );
在内存中的分布:
Stirng对象的空值 表示引用为空(null),没有初始化,没有分配内存空间
判断字符串非空:字符串不为null,且字符内容不能为空字符串(“”)
 
1 2 3 public  static  boolean  hasLength (String str)      return  str != null  && !"" .equals(str.trim()); } 
比较字符串操作:
1 2 3 4 "ABCD"  == "ABCD"  "ABCD"  == new  String("ABCD" ) "ABCD" .equals("ABCD" ) "ABCD" .equals(new  String("ABCD" )) 
String类型常用方法 
int length()返回次字符串的字符个数char charAt(int index)返回指定索引位置的字符int indexOf(String str)返回指定子字符串在此字符串中第一次出现处的索引位置boolean equals(Object anObject)比较内容是否相同boolean equalsIgnoreCase(String anotherString)忽略大小写,比较内容是否相同String toUpperCase() 把当前字符串转换为大写String toLowerCase() 把当前字符串转换为小写String substring(int beginIndex)葱绿指定位置开始截取字符串String substring(int beginIndex, int endIndex)截取指定区域的字符串 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 String str = "HelloWorld" ; System.out.println(str.length()); char  ch = str.charAt(3 );System.out.println(ch); int  index = str.indexOf("lo" );System.out.println(index); System.out.println("helloworld" .equals(str));  System.out.println("helloworld" .equalsIgnoreCase(str)); System.out.println(str.toLowerCase()); System.out.println(str.toUpperCase()); String str1 = str.substring(3 ); System.out.println(str1);  String str2 = str.substring(3 , 6 ); System.out.println(str2);  
可变字符串 StringBuilder(拼接多个字符串的时候才会使用) 性能比String高2500倍
StringBuffer和 StringBuilder的区别:
StringBuffer StringBuffer中的方法都使用了 sunchronized 修饰,保证线程安全但性能较低 
StringBuilder StringBuilder中的方法没有使用 synchronized修饰,线程不安全但是性能较高,开发中建议使用StringBuilder 
 
StringBuilder sb = new StringBuilder(40);创建指定容量的字符数组,缺省为16
// 添加各种数据类型sb.append(123)
// 删除下标start开始到end前一位的字符sb.delete(start, end)
// 删除指定位置的字符sb.deleteCharAt(index)
// 在指定位置插入各种数据类型sb.insert(index, "l")
// 反转字符串
Date日期 1 2 3 4 java.util.Date d = new  java.util.Date(); d.toString();  d.toLocaleString();  long  time = d.getTime(); 
格式化(Format):Date类型转换为String类型, String format(Date date)Date parse(String source)
日期模式列举
日期格式 
日期 
 
 
yyyy-MM-dd 
2020-12-12 
 
HH:mm:ss 
20:12:12 
 
yyyy-MM-dd HH:mm:ss 
2020-12-12 20:12:12 
 
yyyy/MM/dd HH:mm:ss 
2020/12/12 20:12:12 
 
yyyy年MM月dd日 HH时mm分ss秒 
2020年12月12日 20时12分12秒 
 
1 2 3 4 5 6 7 8 9 10 11 public  static  void  main (String[] args)  throws  ParseException 	Date d = new  Date(); 	 	SimpleDateFormat sdf = new  SimpleDateFormat(); 	sdf.applyPattern("yyyy-MM-dd HH:mm:ss" ); 	String dateString = sdf.format(d); 	System.out.println(dateString); 	 	Date dd = sdf.parse(dateString); 	System.out.println(dd); } 
Calendar Calendar是日历类,主要用来对日期做加减法,重新设置日期时间的功能,Calendar本身是抽象类,通过getInstance方法获取对象,底层创建的是Calendar的子类对象。
Calendar类中的MONTH的一月是从0开始计算的。
The first month of the year in the Gregorian and Julian calendars is JANUARY which is 0; the last depends on the number of months in a year.
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  static  void  main (String[] args)  throws  ParseException 	 	Calendar c = Calendar.getInstance(); 	System.out.println(c.get(Calendar.MONTH)); 	 	String time = "2019-09-30" ; 	 	SimpleDateFormat sdf = new  SimpleDateFormat(); 	sdf.applyPattern("yyyy-MM-dd" ); 	Date date = sdf.parse(time); 	 	 	c.setTime(date); 	System.out.println(c.get(Calendar.YEAR)); 	 	System.out.println(c.get(Calendar.MONTH)); 	System.out.println(c.get(Calendar.DAY_OF_MONTH)); 	System.out.println(c.get(Calendar.DAY_OF_YEAR)); 	System.out.println(c.get(Calendar.DAY_OF_WEEK)); } 
正则表达式 匹配规则一:
No. 
规范 
描述 
 
 
1 
\ 
表示反斜线(\)字符 
 
2 
\t 
表示制表符 
 
3 
\n 
表示换行 
 
4 
[abc] 
字符a、b或c 
 
5 
[^abc] 
除了a、b、c之外的任意字符 
 
6 
[a-zA-Z0-9] 
表示由字母、数字组成 
 
7 
\d 
表示数字 
 
8 
\D 
表示非数字 
 
9 
\w 
表示字母、数字、下划线 
 
10 
\W 
表示非字母、数字、下划线 
 
11 
\s 
表示所有空白字符(换行、空格等) 
 
12 
\S 
表示所有非空白字符 
 
13 
^ 
行的开头 
 
14 
$ 
行的结尾 
 
15 
. 
匹配除换行符之外的任意字符 
 
匹配规则二:
数量表示(X表示一组规范)
No. 
规范 
描述 
 
 
1 
X 
必须出现一次 
 
2 
X? 
可以出现0次或者1次 
 
3 
X* 
可以出现0次、1次或多次 
 
4 
X+ 
可以出现1次或者多次 
 
5 
X{n} 
必须出现n次 
 
6 
X{n,} 
必须出现n次以上 
 
7 
X{n,m} 
必须出现n~m次 
 
逻辑运算符(X,Y表示一组规范)
No. 
规范 
描述 
 
 
1 
XY 
X规范后跟着Y规范 
 
2 
X 
Y 
 
3 
(X) 
作为一个捕获组规范 
 
异常 
Throwable类有两个子类 Error和 Exception,分别表示错误和异常。
Error表示代码运行时, JVM出现的问题,如系统崩溃或内存溢出等,不需要处理Error,
StackOverflowError 当应用程序递归太深而发生堆栈溢出,抛出该错误。比如死循环或者没有出口的递归调用。 
OutOfMemoryError 因为内存溢出或者没有可用的内存提供给垃圾回收器时,Java虚拟机无法分配一个对象,这时候抛出该错误,比如new了一个非常庞大数量的对象而没释放。 
 
 
 
抛出异常之后的代码不会执行。 
常见的Exception
ArrayIndexOutOfBoundsException:数组越界 
ArithmeticException:异常运算条件,比如除数为0 
NullPointException:需要对象的地方使用了null,空指针异常。比如调用null对象的实例方法 
 
捕获单个异常 异常一旦产生,首先会实例化一个该类型异常对象,并把该对象赋值给对应catch语句块里的异常类变量。
1 2 3 4 5 6 try  {    a / b       } catch  (ArithmeticException e) {       } 
也可以使用Exception接受所有的异常对象(多态),捕获异常的时候不建议使用Throwable,使用Throwable没有问题,但是Throwable分为Error和Exception,Error是没必要处理的。所以用Exception
方法 
方法说明 
 
 
String getMessage() 
返回异常信息 
 
void printStackTrace() 
打印异常类名和异常信息,以及程序中出现异常的位置 
 
getMessage方法获取异常的错误信息,一般获取后,把错误信息打印给用户查看
printStackTrace方法,用于打印异常具体信息,包含了异常信息,错误类型,错误位置,方便开发阶段的调试,也是JVM默认的异常处理机制
1 2 3 4 5 6 7 8 public  static  void  divide (int  a, int  b)      try  {         a/b     } catch  (ArithmeticException e) {         e.printStackTrace();         System.out.println("异常信息:" +e.getMessage());     } } 
目前直接使用e.printStackTrace()即可
 
捕获多个异常 1 2 3 4 5 6 7 8 9 try  {     } catch  (A异常) {      } catch  (B异常) {      } catch  (C异常) {      } 
finally语句块 try-catch-finally 格式
1 2 3 4 5 6 7 try  {} catch  (异常类型 变量) { } finally  {      } 
try catch或者try finally 必须成对出现
 
一般,把关闭资源的代码放在finally中,保证资源总是能关闭。
throws 异常可以抛出,使用throws关键字
1 修饰符 返回值类型 方法名(参数列表...) throws  异常类A,异常类B{} 
throw 方法内,需要返回一个错误结果给调用者时,使用throw关键字在方法内手动抛出一个具体的异常对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  static  void  main (String[] args)  	try  { 		isExist("will" ); 	} catch  (Exception e) { 		System.out.println(e.getMessage()); 	} } 	 public  static  boolean  isExist (String username)  throws  Exception		String[] data = {"will" , "lucy" , "lily" }; 	if  (username != null  && !username.isEmpty()) { 		for  (String string : data) { 			if  (string.equals(username)) { 				throw  new  Exception("对不起,用户名已存在" ); 			} 		} 	} 	return  false ; } 
throws: 用于提醒isExist方法的调用者,需要处理异常,用于方法声明
异常体系 checked(编译)异常,runtime(运行)异常
RuntimeException和其子类属于运行异常,除了运行异常,其他都是编译异常。
运行异常在编译时期无法检查出来,如空指针异常、除数为0异常。可以避免。可以不用try-catch和throws处理运行异常。
程序员必须处理编译异常,使用try-catch或者throws处理异常
自定义异常类 两种方式:集成Exception或者RuntimeException类,一般推荐集成RuntimeException
需要提供一个无参构造器和一个带String类型参数的构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public  class  CustomerException  extends  RuntimeException  	private  Customer customer; 	public  CustomerException ()   	} 	 	public  CustomerException (String message, Customer customer)   		super (message);  		this .customer = customer; 	} 	 	public  Customer getCustomer ()   		return  customer; 	} 	public  void  setCustomer (Customer customer)   		this .customer = customer; 	} } public  class  Test  	public  static  void  someMistakes (String name)   		throw  new  CustomerException("用户名已存在" , new  Customer(name)); 	} 	 	public  static  void  main (String[] args)   		try  { 			someMistakes("will" ); 		} catch  (Exception e) { 			if  (e instanceof  CustomerException) { 				CustomerException exception = (CustomerException)e; 				System.out.println(exception.getMessage()); 				System.out.println(exception.getCustomer().name); 			} 		} 	} } 
异常链 1 2 3 public  HighAgeException (String message, Throwable cause)  	super (message, cause); } 
在catch到异常后,由于可能会不清楚该异常,则使用另一个异常描述类,重新编辑message,并将原有异常作为第二个参数构造新异常对象,并抛出。如此做,会将异常更加清晰的抛出。让调用者更加直观。
此种方式称为异常链。
多线程 运行一个简单的java程序的时候,存在2个线程:主线程和垃圾回收线程。
线程的创建和启动:
方式一:
自定义类继承Thread 
覆写run方法 
创建自定义类对象 
自定义类对象调用start方法 
 
1 2 3 4 5 6 7 8 9 10 11 12 class  MyThread  extends  Thread      public  void  run ()                } } public  class  ExceptionDemo      public  static  void  main (String[] args)           MyThread t = new  MyThread();         t.start();      } } 
方式二:
通过实现Runnable接口 
覆写run方法 
创建自定义类对象 
把自定类对象作为Thread类构造器参数,调用Thread对象start方法 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 class  MyRunnable  implements  Runnable      public  void  run ()                } } public  class  ThreadDemo2      public  static  void  main (String[] args)           MyRunnable target = new  MyRunnable();         Thread t = new  Thread(target);         t.start();     } } 
启动线程必须调用线程类Thread中的start方法,改方法由Thread类的一个实例来调用,底层会调用该线程的run方法
 
线程生命周期和状态 
新建状态:new一个线程对象后,该线程对象处于新建状态,此时他和其他java对象并无不同。 
可运行状态:RUNNABLE状态分为READY和RUNNING。分别表示就绪和可运行状态。
就绪状态:当线程对象调用start()方法后,该线程处于就绪状态,进入线程队列排队。何时运行取决于CPU调度器的调度 
运行状态:线程对象被CPU调度器调度,执行线程体,就绪和运行状态会相互切换。 
 
 
阻塞状态:正在运行的线程遇到特殊情况,同步、等待I/O操作完成等,进入阻塞状态,让出CPU资源,暂时停止自己的执行。 
等待状态:一个可运行状态线程变成等待状态,它会等待另一个线程通知它转到可运行状态,才继续执行。 
计时等待状态:进入等待状态的线程如果没有其他线程唤醒,此时考虑设计一个提示器,会在特定时间后唤醒该线程对象。 
终止状态:死亡状态,表示线程终止,当线程成功执行完成或者抛出未捕获的Exception或者Error或者调用线程的stop方法(易导致死锁、不推荐) 
 
操作线程的方法: 
join方法,同步,可以使得线程之间的并行执行变成串行执行 
 
A线程中调用了B线程的join()方法时,只有当B线程执行完毕时,A线程才能继续执行。
让正在执行的线程暂停一段时间,进入阻塞状态,常用来模拟网络延迟sleep(long milllis) throws InterruptedException; // 毫秒为单位
setPriority(int x)和getPriority() 
 
优先级和线程的执行机会的次数多少有关,并不是优先级高的就一定先执行。调用方法设置优先级。
后台线程,一本用于为其他线程提供服务,也叫守护线程。JVM的垃圾回收就是典型的后台线程。setDaemon(true)来设置后台线程。
多线程安全安全问题 继承方式:
Java类是单继承,如果继承了Thread类,就不能再有其他直接父类了 
继承方式无法让多线程共享同一个资源 
 
实现方式:
Java类可以实现多个接口,此时该类还可以继承其他类,还可以实现其他接口,设计上更优雅 
多线程可以共享同一个资源 
 
什么时候线程不安全:
多线程 
共享同一个资源 
非原子性操作 
 
线程同步 在多线程访问同一资源的时候,由于一些复杂的操作,导致可能A未访问完全,B就可以拿到资源访问,使得A的一些后续操作作废,因此需要上锁,作用:A开始访问后上锁,B、C只能等待A完全访问后,才能获得资源访问权。
线程同步的两种方式:
同步代码块 同步锁,又称同步监听对象,同步监听器
资源在哪里就锁哪里,如果访问的资源在方法区就锁类,如果访问的资源在堆就锁对象 
任何时候,最多允许一个线程拥有同步锁,谁拿到锁就执行,其他的线程只能在代码块外等着。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public  class  Person  implements  Runnable  	private  Integer appleNum = 50 ; 	 	public  void  run ()   		try  { 			for  (int  i = 0 ; i < 50 ; i++) { 				synchronized  (this ) { 					if  (appleNum > 0 ) { 						Thread.sleep(10 ); 						System.out.println(Thread.currentThread().getName() + "吃了编号为:"  + appleNum-- + "的苹果" ); 					} 				} 			} 		} catch  (InterruptedException e) { 			e.printStackTrace(); 		} 	} } public  class  Test  	public  static  void  main (String[] args)   		Person p1 = new  Person(); 		Thread t1 = new  Thread(p1, "小A" ); 		Thread t2 = new  Thread(p1, "小B" ); 		Thread t3 = new  Thread(p1, "小C" ); 		t1.start(); 		t2.start(); 		t3.start(); 	} } 
同步方法 使用synchronized修饰的方法,叫做同步方法,
1 2 3 synchronized  public  void  doWork ()       } 
此时同步锁是谁:
对于非static方法,同步锁就是this 
对于static方法,同步锁就是当前方法所在的类的字节码对象 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  Person  implements  Runnable  	private  Integer appleNum = 50 ; 	 	synchronized  public  void  run ()   		try  { 			for  (int  i = 0 ; i < 50 ; i++) { 					if  (appleNum > 0 ) { 						Thread.sleep(10 ); 						System.out.println(Thread.currentThread().getName() + "吃了编号为:"  + appleNum-- + "的苹果" ); 					} 			} 		} catch  (InterruptedException e) { 			e.printStackTrace(); 		} 	} } 
synchronized的优劣 好处:线程安全
建议:尽量减小synchronized的作用域
集合 泛型 泛型方法:
1 public  <T> List<T> parse (String url, Class<T> type)  
public 与 返回值中间的  非常重要,可以理解为声明此方法为泛型方法。  
只有声明了  的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法  
 表明该方法将使用泛型类型 T ,此时才可以在方法中使用泛型类型 T 与泛型类一样, T 类型 E 元素 K 键 V 值 
 
数组、可变数组 掌握ArrayList的源代码,动态缩容优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 package  com.lizhaoloveit._集合._ArrayList2;@SuppressWarnings ("unchecked" )public  class  ArrayList <E > 	 	private  E[] elements; 	 	private  static  int  size; 	 	private  static  final  int  DEFAULT_CAPACITY = 10 ; 	 	public  static  final  int  ELEMENT_NOT_FOUND = -1 ; 	 	 	public  ArrayList ()   		elements = (E[]) new  Object[DEFAULT_CAPACITY]; 	} 	 	public  ArrayList (int  capacity)   		if  (capacity < DEFAULT_CAPACITY) { 			capacity = DEFAULT_CAPACITY; 		} 		elements = (E[]) new  Object[capacity]; 	} 	 	 	public  void  clear ()   		size = 0 ; 	} 	 	 	public  boolean  isEmpty ()   		return  size == 0 ; 	} 	 	 	public  boolean  contains (E element)   		return  indexOf(element) != ELEMENT_NOT_FOUND; 	}  	 	 	public  int  indexOf (E element)   		 		if  (element == null ) { 			for  (int  i = 0 ; i < size; i++) { 				if  (elements[i] == null ) return  i; 			} 		} 		for  (int  i = 0 ; i < size; i++) { 			if  (elements[i].equals(element)) return  i; 		} 		return  ELEMENT_NOT_FOUND; 	} 	 	 	public  void  add (E element)   		add(size, element); 	} 	 	 	public  void  add (int  index, E element)   		 		checkRangeForAdd(index); 		 		ensureCapacity(size + 1 ); 		 		for  (int  i = size; i >= index; i--) { 			elements[i] = elements[i - 1 ]; 		} 		 		elements[index] = element; 	} 	 	 	public  E get (int  index)   		checkRange(index); 		return  elements[index]; 	} 	 	 	public  E set (int  index, E element)   		checkRange(index); 		E old = elements[index]; 		elements[index] = element; 		return  old; 	} 	 	 	public  E remove (int  index)   		checkRange(index); 		E old = elements[index]; 		 		for  (int  i = index; i < size - 1 ; i++) { 			elements[i] = elements[i + 1 ]; 		} 		elements[size - 1 ] = null ; 		size--; 		return  old; 	} 	 	 	public  E remove (int  index)   		checkRange(index);       E oldValue = elementData(index);       int  numMoved = size - index - 1 ;       if  (numMoved > 0 )           System.arraycopy(elementData, index+1 , elementData, index,                            numMoved);       elementData[--size] = null ;        return  oldValue;   } 	 	 	public  int  remove (E element)   		int  index = indexOf(element); 		remove(index); 		return  index; 	} 	 	 	private  void  ensureCapacity (int  capacity)   		int  oldCapacity = elements.length; 		if  (oldCapacity > capacity)  			return ; 		 		int  newCapacity = oldCapacity + oldCapacity >> 1 ; 		E[] newElements = (E[])new  Object[newCapacity]; 		for  (int  i = 0 ; i < oldCapacity; i++) { 			newElements[i] = elements[i]; 		} 		elements = newElements; 		System.out.println("旧数组容量:"  + oldCapacity + ",变为新数组容量:"  + newCapacity); 	} 	 	 	private  void  checkRange (int  index)   		if  (index < 0  || index >= size) { 			throw  new  IndexOutOfBoundsException("Index: "  + index + ", size:"  + size); 		} 	} 	 	private  void  checkRangeForAdd (int  index)   		if  (index < 0  || index > size) { 			throw  new  IndexOutOfBoundsException("Index: "  + index + ", size:"  + size); 		} 	} 	 	public  String toString ()   		 		StringBuilder sb = new  StringBuilder(); 		sb.append("size=" ).append(size).append(", [" ); 		for  (int  i = 0 ; i < size; i++) { 			if  (i != 0 ) { 				sb.append(", " ); 			} 			sb.append(elements[i]); 		} 		sb.append("]" ); 		return  sb.toString(); 	} } 
栈 栈结构仅允许在表的一端进行插入和删除操作,这一端被称为栈顶。
栈底、入栈、出栈、
哈希表 数组中的元素在数组中的索引位置是随机的,元素取值和元素的位置之间不存在确定的对应关系,在数组中查找特定的值时,需要把查找值和一系列元素比较。
查询效率取决于查找过程中的比较次数,比较次数越多,效率越低。
如果元素和索引之间存在对应关系,则查找效率会非常高。index = hash(value);
通过给定元素值,调用hash(value)方法就能找到数组中value的位置 index
这种关系叫做hash,往哈希表中存储对象的时候,该hash算法就是对象的hashCode方法。
树和二叉树(还需学习) !还需学习!
集合框架体系 Set 集合 不允许重复元素  继承Collection
List 列表 允许重复元素  继承Collection
Map 映射 value可以重复,Key不行  接口
List接口 List接口规范:要求该容器允许记录元素的添加顺序,也允许元素重复
ArrayList:数组列表, 
LinkedList:链表,表示双向列表和双向队列结构,采用链表实现,使用不多 
Stack类:栈,栈结构,数组实现 
Vector类:向量,采用数组实现,使用不多 
 
List常用API:
添加操作:
boolean add(Object e) 将元素添加到列表末尾void add(int index, Object element) 在列表的index位置插入指定元素boolean addAll(Collection c) 把C列表中的所有元素添加到当前列表中 
删除操作:
Object remove(int index) 从列表中删除指定索引位置的元素,并返回被删除的元素boolean removeAll(Collection c) 从列表中移除C列表中的所有元素 
修改操作:
Object set(int index, Object element)修改列表中指定索引位置的元素,返回被替换的旧元素
查询操作:
int size()返回当前列表中的元素个数boolean is Empty() 判断当前列表中元素个数是否为0Object get(int index) 查询列表中指定索引位置对应的元素Object[] toArray() 把列表对象转换为Object数组boolean contains(Object o) 判断列表是否存在指定对象
集合元素迭代 
集合元素遍历。
并发修改异常(ConcurrentModificationException)
当遍历集合时,删除某集合元素会触发该异常。如果要遍历时删除集合元素,必须使用迭代器。
实现Interface Iterator<E>接口的类可以调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  static  void  main (String[] args)  	List<String> list = new  ArrayList<String>(); 	list.add("1" ); 	list.add("2" ); 	list.add("3" ); 	list.add("4" ); 	list.add("5" ); 	 	Iterator<String> it = list.iterator(); 	while  (it.hasNext()) { 		String ele = it.next(); 		if  (ele.equals("1" )) { 			list.remove(ele); 		} 	} } 
Set接口 Set是 Collection 子接口,Set接口定义了一种规范,该容器不记录元素的添加顺序,不允许元素重复。
常用实现类:
HashSet类:底层采用哈希表实现,开发中使用最多的实现类。 
TreeSet类:底层采用红黑树实现,可以对集合中的元素排序,使用不多。 
 
HashSet 详见HashMap实现原理 
底层采用哈希表实现,元素对象的 hashCode值决定了在哈希表中的存储位置。
当,往 HashSet 中添加新元素时,会先判断该对象的 hashCode 和 集合对象中的 hashCode 值进行比较。
相等:再继续判断新对象和集合对象中的 equals 比较
true 视为同一个对象,则不保存。 
false 存储之前对象同槽位的链表上。 
 
 
不等:直接把该对象存储到 hashCode 指定的位置。 
 
哈希表中元素对象的 hashCode 和 equals 方法很重要
每一个存储到哈希表中的对象,都得覆盖 hashCode 和 equals 方法来判断是否是同一对象,Eclipse可以根据对象哪些字段做比较而自动生成 hashCode 和 equals 方法。
一般的,equals 为 true 的时候 hashCode 也应该相等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Set<String> set = new  HashSet<String>(); set.add("1" ); set.add("2" ); set.add("3" ); set.add("4" ); set.add("5" ); set.size(); set.contains("3" ); set.remove("1" ); for  (String string : set) {	System.out.println(string); } 	 Iterator<String> it = set.iterator(); while  (it.hasNext()) {	String ele = it.next(); 	System.out.println(ele); } Integer[] i = new  Integer[] {1 , 2 , 3 }; List<Integer> list1 = Arrays.asList(i); 
TreeSet TreeSet 除了支持实现接口Comparable的类排序以外,还可以自定义排序器
排序器类需要实现接口 Comparator<T>
1 2 3 public  interface  Comparator <T >     int  compare (T o1, T o2)  } 
compare返回0表示两个对象为同一个对象,返回正数排前面, 返回负数排后面
1 2 3 4 5 6 7 class  NameLengthComparator  implements  java .util .Comparator <User > } public  static  void  main (String[] args)      Set<User> set = new  TreeSet<>(new  NameLengthComparator()); } 
HashSet 等值查询效率高, TreeSet 做范围查询效率高。
Map 映射 
Map常用的API方法:
添加操作:
boolean put(Object key, Object value) 存储一个键值对boolean putAll(Map m) 把 m 中的所有键值对添加到当前Map中 
删除操作:
Object remove(Object key) 从Map中删除指定key的键值对,并返回被删除的key对应的value
修改操作:
同添加操作,使用相同的key 不同的value即可
查询操作:
int size() 返回当前Map中键值对个数boolean isEmpty() 判断当前Map中键值对个数是否为0Object get(Object key) 返回Map中指定key对应的value值,如果不存在该key,返回nullboolean containsKey(Object key) 判断Map中是否包含该Keyboolean containsValue(Object value)判断Map中是否包含指定valueSet keySet() 返回Map中所有key所组成的 Set集合Collection values() 返回Map中所有value所组成的Collection集合Set<Entry>entrySet() 返回Map中所有键值对所组成的Set集合 
HashMap HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了哈希表中的存储位置,因为Map中的key是Set,
map迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import  java.util.*;import  java.util.Map.Entry;public  class  Test  	public  static  void  main (String[] args)   		Map<String, String> map = new  HashMap<String, String>(); 		map.put("g1" , "王昭君" ); 		map.put("g2" , "西施" ); 		map.put("g3" , "貂蝉" ); 		map.put("g4" , "杨玉环" ); 		System.out.println("map中有多少键值对:"  + map.size()); 		System.out.println("是否包含调查哦:"  + map.containsValue("貂蝉" )); 		map.put("g4" , "小乔" ); 		map.remove("g4" ); 		 		Set<String> keys = map.keySet(); 		 		Collection<String> values = map.values(); 		 		Set<Entry<String, String>> entrys = map.entrySet(); 		for  (Entry<String, String> entry : entrys) { 			String key = entry.getKey(); 			String value = entry.getValue(); 			System.out.println("key:"  + key + "--->value:"  + value); 		} 		 		 		String str = "ASWQSASDAWQEAASGVDZGDFGFDBCBVCNGHFIUYTGFBFDGDDFS" ; 		Map<Character, Integer> map2 = new  HashMap<Character, Integer>(); 		for  (int  i = 0 ; i < str.length(); i++) { 			Character key = str.charAt(i); 			if  (map2.containsKey(key)) { 				map2.put(key, map2.get(key) + 1 ); 			} else  { 				map2.put(key, 1 ); 			} 		} 		Set<Entry<Character, Integer>> entrys2 = map2.entrySet(); 		for  (Entry<Character, Integer> entry : entrys2) { 			Character key = entry.getKey(); 			Integer value = entry.getValue(); 			System.out.println("key:"  + key + "--->value:"  + value); 		} 	} } 
TreeMap TreeMap底层基于红黑树算法,key是Set,会对存储的key进行排序,和TreeSet一样
1 2 3 4 5 6 7 8 9 10 11 12 public  class  App      public  static  void  main (String[] args)           Map<String, Stirng> map = new  HashMap<>();         map.put("girl4" , "杨玉环" );         map.put("girl3" , "王昭君" );         map.put("girl2" , "西施" );         map.put("girl1" , "貂蝉" );                  map = new  TreeMap<>(map);         System.out.println(map);     } } 
集合框架工具类 Arrays类是数据工具类,有一个方法比较常用
public static <T> List <T> asList(T... a) 该方法可以把一个Object数组转换为List集合
1 2 3 4 5 6 public  ArraysDemo {    public  static  void  main (String[] args)                    List<Integer> list1 = Arrays.asList(1 , 2 , 3 );     }    } 
通过Arrays.asList 方法得到的List对象的长度是固定的。
Collections 是集合的工具类,封装了 Set、List、Map操作的工具方法,比如拷贝、排序、搜索、比较大小等。
小结 
I/O File类 文件和文件夹目录,表示磁盘中某个文件和文件夹的路径,用于文件的创建、删除、重命名、判断是否存在等方法。
Unix:严格区分大小写,使用/来表示路径分隔符 
Windows:默认情况下 不区分大小写,使用\分割目录路径,java中一个\表示转义,所以Windows系统中需要使用两个\\。 
 
常用方法:
String getName() 获取文件名称String getPath() 获取文件路径String getAbsolutePath() 获取文件的绝对路径File getParentFile() 获取上级目录文件boolean exists() 判断文件是否存在boolean isFile() 是否是文件boolean isDirectory() 判断是否是目录boolean delete() 删除文件boolean mkdirs() 创建当前目录和上级目录File[] listFiles() 列出所有文件对象 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import  java.io.File;public  class  FileDemo  	public  static  void  main (String[] args)   		File f = new  File("/Users/Ammar/Desktop/sddd/123.txt" ); 		System.out.println(f.getName());  		System.out.println(f.getPath());  		System.out.println(f.getAbsolutePath());  		System.out.println(f.getParentFile().getName());  		System.out.println(f.exists()); 		System.out.println(f.isFile()); 		System.out.println(f.isDirectory()); 		 		if  (!f.getParentFile().exists()) { 			f.getParentFile().mkdirs(); 		}  		 		File[] fs = f.getParentFile().getParentFile().listFiles(); 		for  (File file : fs) { 			System.out.println(file); 		} 		list(f.getParentFile().getParentFile()); 	} 	 	public  static  void  list (File file)   		if  (file.isDirectory()) { 			 			File[] files = file.listFiles(); 			if  (files != null ) { 				for  (File file2 : files) { 					list(file2); 				} 			} 		} 		System.out.println(file); 	} } 
字符编码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import  java.io.UnsupportedEncodingException;import  java.util.Arrays;public  class  CodeDemo  	public  static  void  main (String[] args)  throws  UnsupportedEncodingException  		String ISO = "ISO-8859-1" ; 		String utf = "UTF-8" ; 		String name = "李朝" ; 		 		byte [] data = name.getBytes(utf); 		System.out.println(Arrays.toString(data));  		 		 		String result = new  String(data, ISO); 		System.out.println(result); 		 		 		data = result.getBytes(ISO); 		System.out.println(Arrays.toString(data)); 		 		 		result = new  String(data, utf); 		System.out.println(result); 	} } 
注意:GBK 和 utf-8 编码,本身都有对第一个字节的头几个位有要求,必须是1,中文是3个字节,因此必须是  111xxxxx  ,因此,有规范的不同,导致 GBK 和 utf-8 无法互相无损转换。
 
IO流操作 
读取数据:文件——->程序, 输入操作 
保存数据:程序——->文件, 输出操作 
 
按流动方向:输入流和输出流
按数据传输单位:字节流和字符流,每次传递一个字节 byte 或 一个字符 char
按功能上划分:分为节点流和处理流,节点流功能单一,处理流功能更强
输入操作:read
流向 
字节流(单位是字节) 
字符流(单位是字符) 
 
 
输入流 
InputStream 
Reader 
 
输出流 
OutputStream 
Writer 
 
操作UI流的模板:
 
创建源或者目标对象(挖井)
输入操作:把文件中的数据流向到程序中,此文件是源,程序是目标 
输出操作:把程序中的数据流向到文件中,此文件是目标,程序是源 
 
创建IO流对象(水管)
输入操作:创建输入流对象 
输出操作:创建输出流对象 
 
具体的IO操作
输入操作:输入流对象的read方法 
输出操作:输出流对象的write方法 
 
关闭资源(勿忘),一旦资源关闭之后,就不能使用流对象了。否则报错
输入操作:输入流对象.close() 
输出操作:输出流对象.close() 
 
 
四大抽象流是不能创建对象的,根据不同的需求创建他们不同的子类对象。比如操作文件时,使用文件流close 方法,释放资源
 
FileOutputStream 继承 OutputStream
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  class  WriteDemo  	public  static  void  main (String[] args)  throws  IOException  		 		 		 		File dest = new  File("/Users/Ammar/Desktop/sddd/123.txt" ); 		 		 		FileOutputStream out = new  FileOutputStream(dest); 		 		 		out.write(65 );  		out.write(66 );  		out.write(67 );  		String str = "李朝" ; 		out.write(str.getBytes("UTF-8" )); 		 		 		out.close(); 	} } 
继承 InputStream
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  class  ReadDemo  	public  static  void  main (String[] args)  throws  Exception  		 		File src = new  File("/Users/Ammar/Desktop/sddd/123.txt" ); 		 		 		FileInputStream input = new  FileInputStream(src); 		 		 		System.out.println((char )input.read());  		System.out.println((char )input.read());	 		System.out.println((char )input.read());  		 		while  (input.read() != -1 ) { 			System.out.print(input.read()); 		} 		 		byte [] buff = new  byte [5 ]; 		int  len = input.read(buff); 		System.out.println(Arrays.toString(buff)); 		System.out.println(len); 		 		input.close(); 	} } 
FileWriter 继承 OutputStreamWriter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  FileWriterDemo  	public  static  void  main (String[] args)  throws  Exception  		 		File file = new  File("/Users/Ammar/Desktop/sddd/123.txt" ); 		 		FileWriter fw = new  FileWriter(file); 		 		fw.write('辛' ); 		fw.write('弃' ); 		fw.write('疾' ); 		String str = "众里寻他千百度,蓦然回首,那人却在灯火阑珊处" ; 		fw.write(str); 		 		 		fw.close(); 	} } 
FileReader 继承 InputStreamReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  FileReaderDemo  	public  static  void  main (String[] args)  throws  Exception  		 		File file = new  File("/Users/Ammar/Desktop/sddd/123.txt" ); 		 		FileReader fr = new  FileReader(file); 		 		while  (fr.read() != -1 ) { 			System.out.println((char )fr.read()); 		} 		 		 		fr.close(); 	} } 
记事本打开某个文件,如果可以看到内容就是文本文件,否则可以暂时认为是二进制文件
操作二进制文件(图片、音频、视频) 必须使用字节流,操作文本文件使用字符流,尤其是操作带有中文的文件,使用字符流不容易导致乱码,如果不清楚属于哪一类型文件,都可以使用字节流。
需求:文件拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public  class  Test  	public  static  void  copy ()   		 		File src = new  File("/Users/Ammar/Desktop/sddd/copy_before.txt" ); 		File des = new  File("/Users/Ammar/Desktop/sddd/copy_after.txt" ); 		 		FileReader in = null ; 		FileWriter out = null ; 		 		try  { 			in = new  FileReader(src); 			out = new  FileWriter(des); 			 			int  length = -1 ;  			char [] buff = new  char [1024 ];  			 			length = in.read(buff); 			while  (length > 0 ) { 				out.write(buff, 0 , length);  				length = in.read(buff);  			} 		} catch  (Exception e) { 			e.printStackTrace(); 		} finally  { 			try  { 				if  (out != null ) { 					out.close(); 				} 			} catch  (Exception e2) { 				e2.printStackTrace(); 			} finally  {     			try  {     				if  (in != null ) {     					     					in.close();     				}     			} catch  (Exception e2) {     				e2.printStackTrace();     			} 			} 		} 	} 	 	public  static  void  main (String[] args)   		copy(); 	} } 
缓冲流 new 流类A(new 流类B)  四大基流都有各自的包装流
BufferedInputStream,BufferedOutputStream,BufferedReader, BufferedWriter
非常重要的包装流——缓冲流
节点流的功能都比较单一,且性能较低,处理流,也称为包装流—装饰实际模式,详见[二十六种设计模式]
缓冲流内置了一个默认大小为8192字节或者字符的缓存区,缓冲区的作用用来减少磁盘的IO操作,拿字节缓冲流举例,比如一次性读取8192个字节到内存中,或者存满8192个字节再输出到磁盘中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package  com.lizhaoloveit._IO._缓冲流;import  java.io.BufferedInputStream;import  java.io.BufferedOutputStream;import  java.io.File;import  java.io.FileInputStream;import  java.io.FileOutputStream;public  class  Test  	public  static  void  main (String[] args)   		 		File src = new  File("/Users/Ammar/Desktop/sddd/copy_before.mp4" ); 		File des = new  File("/Users/Ammar/Desktop/sddd/copy_after.mp4" ); 		 		BufferedInputStream in = null ; 		BufferedOutputStream out = null ; 		try  { 			in = new  BufferedInputStream(new  FileInputStream(src), 8192 ); 			out = new  BufferedOutputStream(new  FileOutputStream(des), 8192 ); 			 			 			int  len = -1 ; 			byte [] buff = new  byte [1024 ]; 			len = in.read(buff); 			while  (len > 0 ) { 				out.write(buff, 0 , len); 				len = in.read(buff); 			} 			 		} catch  (Exception e) { 			e.printStackTrace(); 		} finally  {     		     		try  {     			in.close();     		} catch  (Exception e) {     			e.printStackTrace();     		}     		try  {     			out.close();     		} catch  (Exception e) {     			e.printStackTrace();     		} 		} 	} } 
对象序列化 序列化,把Java堆内存中的对象数据,通过某种方式把兑现该数据存储到磁盘文件中。序列化在分布式系统中应用非常广泛
反序列化,把磁盘文件中的对象的数据或者把网络节点上的对象数据恢复成Java对象的过程
需要做序列化的类必须实现序列化接口:java.io.Serializable
可以通过IO中的对象流来做序列化和反序列化操作。
ObjectOutputStream:通过writeObject方法做序列化操作
如果字段使用 transient 修饰则不会被序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package  com.lizhaoloveit._IO._serializable;import  java.io.File;import  java.io.FileInputStream;import  java.io.FileOutputStream;import  java.io.ObjectInputStream;import  java.io.ObjectOutputStream;public  class  Test  	public  static  void  main (String[] args)   		File file = new  File("/Users/Ammar/Desktop/sddd/obj.txt" ); 		ObjectOutputStream out = null ; 		try  { 			out = new  ObjectOutputStream(new  FileOutputStream(file)); 			User u = new  User("Will" , "1111" , 17 ); 			out.writeObject(u); 		} catch  (Exception e) { 			e.printStackTrace(); 		} finally  {     		try  {     			if  (out != null ) {     				out.close();     			}     		} catch  (Exception e) {     			e.printStackTrace();     		} 		} 	 		 		ObjectInputStream in = null ; 		try  { 			in = new  ObjectInputStream(new  FileInputStream(file)); 			Object obj = in.readObject(); 			 			System.out.println(obj); 		} catch  (Exception e) { 			e.printStackTrace(); 		} finally  {     		try  {     			if  (in != null ) {     				in.close();     			}     		} catch  (Exception e) {     			e.printStackTrace();     		} 		} 	} } 
序列化版本问题 当类实现 Serializable 接口后,编译时会根据字段生成一个缺省的 serialVersionUID值,并在序列化操作时,写到序列化数据文件中。
随着项目升级系统的class文件也会升级,此时从新编译,serialVersionUID值又会改变,所以在反序列化时,JVM会把对象数据中的 serialVersionUID 与本地字节码中的 serialVersionUID 进行比较,如果不同,意味着版本不同,会报异常 InvalidCastException。 类版本不对应,不能反序列化。如果版本号相同 则可以反序列化。
图
为了避免代码版本升级造成反序列化不兼容,开发中可以故意在类中提供一个固定的 serialVersionUID值。
打印流 打印流是一种特殊的输出流,可以输出任意类型的数据,可以作为处理流包装一个平台的节点流使用
PrintStream 字节打印流 
PrintWriter 字符打印流 
 
print方法,打印不换行,
println方法,打印后换行。
标准IO 通过键盘输入 称为标准输入
通过屏幕上显示程序数据称为 标准输出
1 2 3 4 5 6 7 public  static  void  main (String[] args)  throws  Exception     Scanner sc = new  Scanner(System.in);     while  (sc.hasNextLine()) {         String line = sc.nextLine();         System.out.println("ECHO:"  + line);     } } 
综合练习:
输出文件中的代码行数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public  static  int  lineCountOfFilePath (String filePath)  	File file = new  File(filePath); 	int  count = 0 ;  	 	if  (file.isDirectory()) { 		File[] fileList = file.listFiles(); 		for  (File f : fileList) { 			count += lineCountOfFilePath(f.getAbsolutePath()); 		} 	} else  { 		 		if  (file.getName().contains(".java" )) { 			Integer fileCount = lineCountOfFile(filePath); 			if  (fileCount != null ) { 				try  { 					Thread.sleep(15 ); 				} catch  (InterruptedException e) { 					e.printStackTrace(); 				} 				System.out.println("File:"  + file.getAbsolutePath() + ", "  + fileCount + "行。" ); 				count += fileCount; 			} 		} 	} 	return  count; } public  static  Integer lineCountOfFile (String filePath)  	 	 	File file = new  File(filePath); 	BufferedReader in = null ; 	int  count = 0 ; 	try  { 		in = new  BufferedReader(new  FileReader(file)); 		while  (in.readLine() != null ) { 			 			count++; 		} 	} catch  (Exception e) { 		e.printStackTrace(); 		return  null ; 	} finally  {     	     	try  {     		if  (in != null ) {     			in.close();     		}     	} catch  (Exception e) {     		e.printStackTrace();     		return  null ;     	} 	} 	return  count; } 
AutoClose java 1.7 以后的新语法,实现 AutoCloseAble 接口的流对象都可使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  Test  	public  static  void  main (String[] args)   		File file = new  File("/Users/Ammar/Desktop/sddd/copy_before.txt" ); 		 		try  (FileReader in = new  FileReader(file); FileWriter out = new  FileWriter(file);) { 			 			int  length = -1 ;  			char [] buff = new  char [1024 ];  			 			length = in.read(buff); 			while  (length > 0 ) { 				out.write(buff, 0 , length);  				length = in.read(buff);  			} 		} catch  (Exception e) { 			e.printStackTrace(); 		} 	} } 
动态编程和可配置文件 使用场景:一个类的成员变量是可变的,不能写死一个类。(硬编码:写死的代码,需要经常改动。)
解决方案,使用一个 a.txt ,其内容为 username=zhangsan1password=123,这个文件就是可配置文件。
配置文件可以解决硬编码。应用场景:框架中大量使用。
配置文件 习惯使用两种配置文件:
properties 文件 / 属性文件 / 资源文件 / 配置文件
以 properties 作为文件名的后缀名,可以存储 key = value 结构的简单数据。 
 
XML 文件,树状结构,存储复杂数据
 
1 2 3 4 <user>     <user>     </user> </user> 
properties 文件的数据存储 基本语法:
配置文件需要跟随字节码,放在 resource 文件夹中, 
配置文件夹中,所有的数据都是字符串类型,而且不需要引号 
配置文件中,不适用空格 
 
使用#表示注释内容
 
properties 文件解析 
properties 是 Map 的实现类,Properties 文件比较特殊,使用 Properties 新增的方法
Source Folder  名字就叫 resources
1 2 3 4 public  void  load (InputStream inStream) public  String getProperty (String key) 
使用相对路径加载资源文件 ,相对于字节码的输出路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public  void  testGetUser ()  throws  Exception     Properties ps = new  Properties();          Thread t = Thread.currentThread();          ClassLoader loader = t.getContextClassLoader();          InputStream in = loader.getResourceAsStream("user.properties" );               ps.load(in);               String username = ps.getProperty("username" );     String password = ps.getProperty("password" );      } 
使用配置文件创建对象 1 className=cn.lizhaoloveit.ps.Student 
1 2 3 4 5 6 7 8 9 @Test public  void  testSleep ()  throws  Exception          InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("person.properties" );     Properties ps = new  Properties();     ps.load(in);     Person p = (Person) Class.forName(ps.getProperty("className" ).newInstance();     p.sleep();  } 
反射的优势:
提高程序灵活性和扩展性 
降低耦合性,提高自适应能力 
允许程序创建和控制任何类,无需提前编码目标类 
 
缺点:
性能:使用反射基本上是一种运行期间解析字节码操作,效率较低,一般程序不建议使用,对灵活性和拓展性要求较高的工具和框架上使用较多 
代码复杂性:反射在运行时,程序员无法再源代码中看到程序的逻辑,反射代码比响应的直接代码更复杂,会带来维护的问题 
 
反射是框架的基石。
 
XML概述 XML(eXtensible Markup Language),一种可扩展的标记语言,用于传输数据。XML是一种通用的数据交换格式,很多系统配置文件都是 XML 任意一个javaEE框架中都要用到 XML 
XML 语法:
只能有一个根标签
 
XML 文档结构 
把 XML 文档加载到内存中,使用 Document 对象来描述整个文档 
所有的标签,使用 Element 描述 
标签的属性,使用 Attr 来描述 
其他文本内容 使用 Text 描述 
 
所有的类有一个抽象的类 Node,在 XML 中一切皆节点 
XML 约束 XML:可拓展的标记语言,可以自定义标签。
约束 XML:dtd 约束, schema 约束
dtd 约束、schema 约束 会告诉我们只能写哪些标签,哪些属性。
运用方式为:正则匹配
DOM树(Document Object Model) 基于 DOM 的 XML 分析器将一个 XML 文档转换成一个对象模型的集合,称为 DOM 树。
优点:树状结构,有助于理解,代码更容易编写,解析过程中,树状结构保存在内存中,方便修改
 
获取DOM对象的步骤和过程 
获取 XML 文件 1 2 3 4 5 @Test public  void  testGetXML ()  throws  Exception     	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); 	System.out.println(doc); 
创建 XML 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 File file = new  File("day02/contacts.xml" ); @Test public  void  testCreateNewDocument ()  throws  Exception 	 	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 	 	Element root = doc.createElement("contacts" ); 	 	doc.appendChild(root); 	 	 	Transformer trans = TransformerFactory.newInstance().newTransformer(); 	 	trans.setOutputProperty(OutputKeys.INDENT, "yes" ); 	trans.transform(new  DOMSource(doc), new  StreamResult(file)); } 
新增联系人信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Test public  void  testInsertNewElement ()  throws  Exception 	Linkman man = new  Linkman("2" , "李朝" , "18" , "广州" , "java" , "lacuself@126.com" ); 	 	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); 	 	Element root = doc.getDocumentElement(); 	 	Element linkmanE = doc.createElement("linkman" );  	Element nameE = doc.createElement("name" );  	Element ageE = doc.createElement("age" );  	Element addressE = doc.createElement("address" );  	Element courseE = doc.createElement("course" );  	Element emailE = doc.createElement("email" );  	 	 	linkmanE.setAttribute("id" , man.getId()); 	nameE.setTextContent(man.getName()); 	ageE.setTextContent(man.getAge()); 	addressE.setTextContent(man.getAddress()); 	courseE.setTextContent(man.getCourse()); 	emailE.setTextContent(man.getEmail()); 	 	 	linkmanE.appendChild(nameE); 	linkmanE.appendChild(ageE); 	linkmanE.appendChild(addressE); 	linkmanE.appendChild(courseE); 	linkmanE.appendChild(emailE); 	root.appendChild(linkmanE); 	 	 	Transformer transformer = TransformerFactory.newInstance().newTransformer(); 	Source xmlSource = new  DOMSource(doc);  	Result outputTarget = new  StreamResult(file); 	transformer.transform(xmlSource, outputTarget); } 
修改 XML 文件中的元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public  void  testSetElement ()  throws  Exception 	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); 	Element root = doc.getDocumentElement(); 	 	Element linkmanE = (Element) root.getElementsByTagName("linkman" ).item(0 ); 	 	Element addressE = (Element) linkmanE.getElementsByTagName("address" ).item(0 ); 	addressE.setTextContent("北京" ); 	 	 	Transformer transformer = TransformerFactory.newInstance().newTransformer(); 	transformer.transform(new  DOMSource(doc), new  StreamResult(file)); } 
删除 XML 文件中的元素 1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public  void  testRemoveElement ()  throws  Exception 	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); 	Element root = doc.getDocumentElement(); 	 	Element linkmanE = (Element) root.getElementsByTagName("linkman" ).item(0 ); 	 	linkmanE.getParentNode().removeChild(linkmanE); 	 	 	Transformer transformer = TransformerFactory.newInstance().newTransformer(); 	transformer.transform(new  DOMSource(doc), new  StreamResult(file)); } 
查询 XML 中的节点和值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public  void  testGetXML ()  throws  Exception 	Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); 	 	Element root = doc.getDocumentElement(); 	 	NodeList linkmanList = root.getElementsByTagName("linkman" ); 	for  (int  i = 0 ; i < linkmanList.getLength(); i++) { 		System.out.println(linkmanList.item(i)); 	} 	 	 	Element ele = (Element) linkmanList.item(0 ); 	Element nameE = (Element) ele.getElementsByTagName("name" ).item(0 ); 	String name = nameE.getTextContent(); 	System.out.println(name); }