本文最后编辑于 前,其中的内容可能需要更新。
JavaSE进阶 面向对象 Final
抽象类和接口 抽象类 抽象类定义:
1 2 3 [修饰符列表] abstract class 类名 { 类体; }
抽象类无法实例化,无法创建对象,所以抽象类用来被子类继承。
final和abstract不能联合使用 ,两个关键字是对立的。
抽象类的子类可以是抽象类,也可以是非抽象类。
抽象类无法实例化,但有构造方法,供子类使用。
抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中 。
抽象方法定义:
1 public abstract void doSome () ;
一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现 。
抽象方法不能有方法体 。
抽象类降低接口实现类与接口之间的实现难度。
接口 接口是一种“引用数据类型”,是完全抽象的。
定义:
接口支持多继承。
接口中只有常量+抽象方法。
接口中所有的元素都是public修饰的
接口中抽象方法的public abstract可以省略。
接口中常量的public static final可以省略。
实现接口 本质:对接口的继承
1 2 3 4 class D implements A ,B ,C { public void m1 () { }
一个非抽象的类,实现接口时,必须将接口中所有方法加以实现 。
一个类可以实现多个接口 。
extends和implements可以共存,extends在前,implements在后。
使用接口,写代码的时候,可以使用多态(父类型引用指向子类型对象)。
package 和 import 关于java语言中的package和import机制:
注意: package语句只允许出现在java源代码的第一行。
包名命名规范:
包名命名规范:
公司域名倒序 + 项目名 + 模块名 + 功能名
import什么时候使用?
A类中使用B类时,A和B类都在同一个包下,不需要import;不在同一个包下。需要使用 import。
java.lang.*;这个包下的类不需要。
import怎么用?
import语句只能出现在package语句之下,class声明语句之上。
import语句还可以采用星号的方式。
访问控制权限 类别: private,public,protected,默认
修饰范围: 属性(4个都能用) 方法(4个都能用) 类(public和默认能用,其它不行。) 接口(public和默认能用,其它不行。) …..
控制范围 private 只能在本类中访问 public 在任何位置都可以访问 默认 只能在本类,以及同包下访问 protected 只能在本类、同包、子类中访问
Object类和内部类 Object类 在Object类中找方法 第一种:去源代码当中。(但是这种方式比较麻烦) 第二种:去查阅java的类库的帮助文档。
什么是API? 应用程序编程接口。(Application Program Interface) 整个JDK的类库就是一个javase的API。 每一个API都会配置一套API帮助文档。 SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)
常用方法:
1 2 3 4 5 protected Object clone() // 负责对象克隆 int hashCode() // 获取对象哈希值 boolean equals(Object obj) // 判断两个对象是否相等 String toString() // 将对象转换成字符串形式 protected void finalize() // 垃圾回收器负责调用的方法
toString()方法 所有类的toString()方法是需要重写的。重写越简单越明了就好。
System.out.println(引用); 这里会自动调用“引用”的toString()方法。
String类是SUN写的,toString方法已经重写了。
equals()方法
注意: 注意检查所有类是否重写了equals方法。
内部类 定义: 在类的内部又定义了一个新的类。被称为内部类。
分类: 静态内部类:类似于静态变量 实例内部类:类似于实例变量 局部内部类:类似于局部变量
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Test01 { static String country; 由于前面有static ,所以称为“静态内部类”: static class Inner1 { } int age; 没有static 叫做实例内部类: class Inner2 { } public void doSome () { int i = 100 ; 局部内部类: class Inner3 { } }
匿名内部类 匿名内部类是局部内部类的一种,因没有名字而得名。
使用例:
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 class Test01 { public static void main (String[] args) { 使用匿名内部类 mm.mySum(new Compute(){ public int sum (int a, int b) { return a + b; } }, 200 , 300 ); 未使用匿名内部类: mm.mySum(new ComputeImpl(), 100 , 200 ); } } interface Compute { int sum (int a, int b) ; } class ComputeImpl implements Compute { public int sum (int a, int b) { return a + b; } }
缺点: 使用内部类编写的代码,可读性很差。能不用尽量不用。
数组 性质
Java语言中的数组是一种引用数据类型 。不属于基本数据类型。数组的父类是Object 。
数组当中可以存储“基本数据类型”的数据,也可以存储“引用数据类型”的数据。
数组是引用类型,所以数组存储在堆 中。
数组中存储“java对象”时,实际上存储的是对象的“引用(内存地址)”,不能直接存储java对象。
数组一旦创建,长度不可变。
数组的分类:一维数组、二维数组、三维数组、多维数组…。
所有的数组对象都有length属性 (java自带的),用来获取数组中元素的个数。
java中的数组要求数组中元素的类型统一。比如Person类型数组只能存储Person类型。
数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续。
数组中首元素的内存地址作为整个数组对象的内存地址。
缺点:
由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,
**效率较低**,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
数组不能存储大数据量 ,因为很难在内存空间上找到一块特别大的连续的内存空间。
声明/定义一维数组 语法格式:
1 2 3 4 5 int [] array1;double [] array2;boolean [] array3;String[] array4; Object[] array5;
初始化一维数组 包括两种方式:静态初始化一维数组,动态初始化一维数组。
静态初始化语法格式: 1 int [] array = {100 , 2100 , 300 , 55 };
动态初始化语法格式: 1 2 3 int [] array = new int [5 ]; String[] names = new String[6 ];
main方法的String数组 main方法上面的String[] args数组主要用来接收用户输入参数。
使用例:
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 package com.javase.array;public class ArrayTest06 { public static void main (String[] args) { if (args.length != 2 ){ System.out.println("使用该系统时请输入程序参数,参数中包括用户名和密码信息,例如:zhangsan 123" ); return ; } String username = args[0 ]; String password = args[1 ]; if ("admin" .equals(username) && "123" .equals(password)){ System.out.println("登录成功,欢迎[" + username + "]回来" ); System.out.println("您可以继续使用该系统...." ); }else { System.out.println("验证失败,用户名不存在或者密码错误!" ); } } }
数组拷贝 调用方法:
数组扩容 java中对数组的扩容:
先新建一个大容量的数组,然后将小容量数组中的数据拷贝到大数组当中。数组扩容效率较低。故应尽可能少的进行数组的拷贝,提高效率。
二维数组 二维数组其实是一个特殊的一维数组 ,特殊在这个一维数组当中的每一个元素是一个一维数组。多维数组同理。
1 a[二维数组中的一维数组的下标][一维数组的下标]
取出二维数组中的一维数组。
静态初始化 1 int [][] array = {{1 ,1 ,1 },{2 ,3 ,4 ,5 },{0 ,0 ,0 ,0 },{2 ,3 ,4 ,5 },{2 ,3 ,4 ,5 },{2 ,3 ,4 ,5 },{2 ,3 ,4 ,5 }};
动态初始化 1 int [][] array = new int [3 ][4 ];
Arrays工具类 包含关于排序和查找的内容
使用例:
1 2 3 4 Arrays.sort(arr); int index = Arrays.binarySearch(arr, 5 );
常用类 String字符串 String表示字符串类型,属于引用数据类型。在JDK当中双引号括起来的字符串例如:”abc” “def”都直接存储在“方法区”的“字符串常量池”当中。
分析以下程序,一共创建了几个对象:
1 2 3 4 5 6 7 8 9 10 11 12 public class StringTest03 { public static void main (String[] args) { String s1 = new String("hello" ); String s2 = new String("hello" ); } }
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class StringTest02 { public static void main (String[] args) { String s1 = "hello" ; String s2 = "hello" ; System.out.println(s1 == s2); String x = new String("xyz" ); String y = new String("xyz" ); System.out.println(x == y); System.out.println(x.equals(y)); } }
String常用方法 String类中的构造方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 第一个:String s = new String("" ); String s6 = new String("helloworld!" ); 第二个:String s = "" ; 最常用 String s1 = "hello world!" ; 第三个:String s = new String(char 数组); char [] chars = {'我' ,'是' ,'中' ,'国' ,'人' }; String s4 = new String(chars); System.out.println(s4); 第四个:String s = new String(char 数组,起始下标,长度); String s5 = new String(chars, 2 , 3 ); 第五个:String s = new String(byte 数组); byte [] bytes = {97 , 98 , 99 }; String s2 = new String(bytes); System.out.println(s2.toString()); System.out.println(s2); 第六个:String s = new String(byte 数组,起始下标,长度) String s3 = new String(bytes, 1 , 2 ); System.out.println(s3);
其他方法
1 2 3 4 5 6 7 8 9 10 int result = "abc" .compareTo("abc" ); System.out.println(result); int result2 = "abcd" .compareTo("abce" ); System.out.println(result2); int result3 = "abce" .compareTo("abcd" ); System.out.println(result3);
(掌握).boolean contains(CharSequence s)
1 2 3 System.out.println("HelloWorld.java" .contains(".java" )); System.out.println("http://www.baidu.com" .contains("https://" ));
(掌握). boolean endsWith(String suffix)
1 2 3 4 System.out.println("test.txt" .endsWith(".java" )); System.out.println("test.txt" .endsWith(".txt" )); System.out.println("fdsajklfhdkjlsahfjkdsahjklfdss" .endsWith("ss" ));
(掌握).boolean equals(Object anObject)
1 2 3 4 System.out.println("abc" .equals("abc" ));
(掌握).boolean equalsIgnoreCase(String anotherString)
1 2 System.out.println("ABc" .equalsIgnoreCase("abC" ));
1 2 3 4 5 byte [] bytes = "abcdef" .getBytes(); for (int i = 0 ; i < bytes.length; i++){ System.out.println(bytes[i]); }
(掌握).int indexOf(String str)
1 2 System.out.println("oraclejavac++.netc#phppythonjavaoraclec++" .indexOf("java" ));
1 2 3 4 String s = "a" ; System.out.println(s.isEmpty());
1 2 3 System.out.println("abc" .length());
(掌握).int lastIndexOf(String str)
1 2 System.out.println("oraclejavac++javac#phpjavapython" .lastIndexOf("java" ));
(掌握). String replace(CharSequence target, CharSequence replacement)
1 2 3 4 5 6 7 String newString = "http://www.baidu.com" .replace("http://" , "https://" ); System.out.println(newString); String newString2 = "name=zhangsan&password=123&age=20" .replace("=" , ":" ); System.out.println(newString2);
(掌握).String[] split(String regex)
1 2 3 4 5 6 7 8 9 10 11 String[] ymd = "1980-10-11" .split("-" ); for (int i = 0 ; i < ymd.length; i++){ System.out.println(ymd[i]); } String param = "name=zhangsan&password=123&age=20" ; String[] params = param.split("&" ); for (int i = 0 ; i <params.length; i++){ System.out.println(params[i]); }
(掌握).boolean startsWith(String prefix)
1 2 3 System.out.println("http://www.baidu.com" .startsWith("http" )); System.out.println("http://www.baidu.com" .startsWith("https" ));
(掌握)、 String substring(int beginIndex)参数是起始下标。
1 2 System.out.println("http://www.baidu.com" .substring(7 ));
(掌握)、String substring(int beginIndex, int endIndex)
1 2 3 System.out.println("http://www.baidu.com" .substring(7 , 10 ));
(掌握)、char[] toCharArray()
1 2 3 4 5 char [] chars = "我是中国人" .toCharArray(); for (int i = 0 ; i < chars.length; i++){ System.out.println(chars[i]); }
(掌握)、String toLowerCase()
1 2 System.out.println("ABCDefKXyz" .toLowerCase());
(掌握)、String toUpperCase();
1 2 System.out.println("ABCDefKXyz" .toUpperCase());
1 2 System.out.println(" hello world " .trim());
(掌握). String中只有一个方法是静态的,不需要new对象:valueOf
1 2 3 4 5 6 7 8 String s1 = String.valueOf(true ); String s1 = String.valueOf(100 ); String s1 = String.valueOf(3.14 ); String s1 = String.valueOf(new Customer()); System.out.println(s1);
StringBuffer和StringBuilder 因为java中的字符串是不可变的,每一次拼接都会产生新字符串。这样会占用大量的方法区内存。造成内存空间的浪费。
1 2 String s = "abc"; s += "hello";
就以上两行代码,就导致在方法区字符串常量池当中创建了3个对象:
“abc””hello””abchello”
需要进行大量字符串的拼接操作,建议使用JDK中自带的:
1 2 java.lang.StringBuffer java.lang.StringBuilder
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class StringBufferTest02 { public static void main (String[] args) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("a" ); stringBuffer.append(3.14 ); stringBuffer.append(true ); StringBuffer sb = new StringBuffer(100 ); sb.append("hello" ); System.out.println(sb); } }
优化性能:
在创建StringBuffer的时候尽可能给定一个初始化容量,减少底层数组的扩容次数,提高程序的执行效率。
StringBuffer和StringBuilder的区别 StringBuffer中的方法都有synchronized关键字修饰。是线程安全的。 StringBuilder中的方法都没有synchronized关键字修饰,是非线程安全的。
StringBuilder/StringBuffer可变的原因 StringBuffer/StringBuilder内部实际上是一个byte[]数组,这个byte[]数组没有被final修饰,其初始化容量为16,存满之后会调用了数组拷贝System.arraycopy()…进行扩容。所以StringBuilder/StringBuffer 适合于使用字符串的频繁拼接操作。
而String类中的byte[]数组被final修饰,不可再指向其它对象,arraycopy失效,故String不可变。
基础类型对应的 8 个包装类 包装类的作用:当方法无法接收基本数据类型的数字时,传对应的包装类进去。
8种基本数据类型对应的包装类型名:
1 2 3 4 5 6 7 8 9 10 基本数据类型 包装类型 ------------------------------------- byte java.lang.Byte(父类Number) short java.lang.Short(父类Number) int java.lang.Integer(父类Number) long java.lang.Long(父类Number) float java.lang.Float(父类Number) double java.lang.Double(父类Number) boolean java.lang.Boolean(父类Object) char java.lang.Character(父类Object)
八种包装类中其中6个都是数字对应的包装类,其父类都是Number,先研究Number中公共的方法: Number是一个抽象类,无法实例化对象。 Number类中有这样的方法:
1 2 3 4 5 6 byte byteValue () 以 byte 形式返回指定的数值。abstract double doubleValue () 以 double 形式返回指定的数值。abstract float floatValue () 以 float 形式返回指定的数值。abstract int intValue () 以 int 形式返回指定的数值。abstract long longValue () 以 long 形式返回指定的数值。short shortValue () 以 short 形式返回指定的数值。
这些方法其实所有的数字包装类的子类都有,负责拆箱。
自动装箱和自动拆箱 在java5之后,引入了一种新特性,自动装箱和自动拆箱
自动装箱:基本数据类型自动转换成包装类。
自动拆箱:包装类自动转换成基本数据类型。
面试题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class IntegerTest06 { public static void main (String[] args) { Integer a = 128 ; Integer b = 128 ; System.out.println(a == b); Integer x = 127 ; Integer y = 127 ; System.out.println(x == y); } }
Integer类中的常用方法 1 2 3 4 5 6 7 8 9 public class IntegerTest07 { public static void main (String[] args) { Integer x = new Integer(1000 ); int y = x.intValue(); System.out.println(y); Integer a = new Integer("123" );
不是“数字”不能包装成Integer。
1 2 Integer a = new Integer("中文" );
重点方法:static int parseInt(String s) 1 2 3 4 5 6 7 8 9 10 11 12 int retValue = Integer.parseInt("123" ); int retValue = Integer.parseInt("中文" ); System.out.println(retValue + 100 ); double retValue2 = Double.parseDouble("3.14" ); System.out.println(retValue2 + 1 ); float retValue3 = Float.parseFloat("1.0" ); System.out.println(retValue3 + 1 );
了解方法:进制转换 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 String binaryString = Integer.toBinaryString(3 ); System.out.println(binaryString); hexString = Integer.toHexString(17 ); System.out.println(hexString); String octalString = Integer.toOctalString(8 ); System.out.println(octalString); System.out.println(new Object()); Integer i1 = Integer.valueOf(100 ); System.out.println(i1); Integer i2 = Integer.valueOf("100" ); System.out.println(i2); } }
使用例:String,int,Integer之间互相转换
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 public class IntegerTest08 { public static void main (String[] args) { int i1 = Integer.parseInt("100" ); System.out.println(i1 + 1 ); String s2 = i1 + "" ; System.out.println(s2 + 1 ); Integer x = 1000 ; int y = x; Integer k = Integer.valueOf("123" ); String e = String.valueOf(k); } }
日期相关类 Date类导包
1 2 import java.util.Date;import java.text.SimpleDateFormat;
获取系统当前时间 1 2 3 4 5 6 7 8 9 10 public class DateTest01 { public static void main (String[] args) throws Exception { Date nowTime = new Date();
日期格式化且Date —> String 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); String nowTimeStr = sdf.format(nowTime); System.out.println(nowTimeStr);
日期字符串String转换成Date类型 1 2 3 4 5 6 7 8 9 String time = "2008-08-08 08:08:08 888" ; SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); Date dateTime = sdf2.parse(time); System.out.println(dateTime); } }
简单总结System类的相关属性和方法:
System.out 【out是System类的静态变量。】
System.out.println() 【println()方法不是System类的,是PrintStream类的方法。】
System.gc() 建议启动垃圾回收器
System.currentTimeMillis() 获取自1970年1月1日到系统当前时间的总毫秒数。
System.exit(0) 退出JVM。
System.currentTimeMillis():获取自1970年1月1日到系统当前时间的总毫秒数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DateTest02 { public static void main (String[] args) { long nowTimeMillis = System.currentTimeMillis(); System.out.println(nowTimeMillis); long begin = System.currentTimeMillis(); print(); long end = System.currentTimeMillis(); System.out.println("耗费时长" +(end - begin)+"毫秒" ); } public static void print () { for (int i = 0 ; i < 1000000000 ; i++){ System.out.println("i = " + i); } } }
数字相关类 数字类导包
1 import java.text.DecimalFormat;
java.text.DecimalFormat:数字格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class DecimalFormatTest01 { public static void main (String[] args) { DecimalFormat df = new DecimalFormat("###,###.##" ); String s = df.format(1234.561232 ); System.out.println(s); DecimalFormat df2 = new DecimalFormat("###,###.0000" ); String s2 = df2.format(1234.56 ); System.out.println(s2); } }
财务大数据BigDecimal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class BigDecimalTest01 { public static void main (String[] args) { BigDecimal v1 = new BigDecimal(100 ); BigDecimal v2 = new BigDecimal(200 ); BigDecimal v3 = v1.add(v2); System.out.println(v3); BigDecimal v4 = v2.divide(v1); System.out.println(v4); } }
Ramdom 导包
1 import java.util.Random;
创建随机数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class RandomTest01 { public static void main (String[] args) { Random random = new Random(); int num1 = random.nextInt(); System.out.println(num1); int num2 = random.nextInt(101 ); System.out.println(num2); } }
Enum枚举类型 枚举是一种引用数据类型 ,用于结果判断。
结果只有两种情况的,建议使用布尔类型。结果超过两种并且还是可以一枚一枚列举出来的,建议使用枚举类型。例如:颜色、四季、星期等。
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 EnumTest02 { public static void main (String[] args) { Result r = divide(10 , 2 ); System.out.println(r == Result.SUCCESS ? "计算成功" : "计算失败" ); } public static Result divide (int a, int b) { try { int c = a / b; return Result.SUCCESS; } catch (Exception e){ return Result.FAIL; } } } enum Result { SUCCESS, FAIL }
异常类 Exception下有两个分支: Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)。 RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)
对异常的处理 第一种:如果希望调用者来处理,选择throws上报。在方法声明的位置上,使用throws关键字,抛给上一级。
第二种:使用try..catch语句进行异常的捕捉。
注意 :
编译时异常和运行时异常,都发生在运行阶段 ,编译阶段异常是不会发生的。
只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
try语句块中的某一行出现异常,该行后面的代码不会执行。
try..catch捕捉异常之后,后续代码可以执行。
throws支持多态
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class ExceptionTest06 { public static void main (String[] args) { System.out.println("main begin" );
try..catch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 try { m1(); System.out.println("hello world!" ); } catch (FileNotFoundException e){ System.out.println("文件不存在,可能路径错误,也可能该文件被删除了!" ); System.out.println(e); } System.out.println("main over" ); } private static void m1 () throws FileNotFoundException { System.out.println("m1 begin" ); m2(); System.out.println("m1 over" ); }
throws后面也可以写多个异常,可以使用逗号隔开
1 2 3 4 5 6 7 8 private static void m2 () throws FileNotFoundException { System.out.println("m2 begin" ); m3(); System.out.println("m2 over" ); }
编译报错分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private static void m3 () throws FileNotFoundException { new FileInputStream("D:\\course\\01-课\\学习方法.txt" ); System.out.println("如果以上代码出异常,这里会执行吗??????????????????不会!!!" ); } }
深入try..catch
catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
catch写多个的时候,从上到下,必须遵守从小到大。
使用例:
多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ExceptionTest07 { public static void main (String[] args) { try { FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf" ); System.out.println("以上出现异常,这里无法执行!" ); } catch (FileNotFoundException e) { System.out.println("文件不存在!" ); } try { FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf" ); } catch (Exception e) { System.out.println("文件不存在!" ); }
catch可以写多个,从上到下,必须遵守从小到大
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 try { FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf" ); fis.read(); } catch (FileNotFoundException e) { System.out.println("文件不存在!" ); } catch (IOException e){ System.out.println("读文件报错了!" ); try { FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf" ); fis.read(); } catch (IOException e){ System.out.println("读文件报错了!" ); } catch (FileNotFoundException e) { System.out.println("文件不存在!" ); } try { FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf" ); System.out.println(100 / 0 ); } catch (FileNotFoundException | ArithmeticException | NullPointerException e) { System.out.println("文件不存在?数学异常?空指针异常?都有可能!" ); } } }
throws和throw的区别 throws: 声明位置 方法名之后
1 2 public void test1 () throws NullPointerExeption {}
作用: 通知开发人员当前方法在运行时,【有可能】抛出的异常。
携带数据 throws后面携带【异常类型】,一个throws后面可以携带多个异常类型。
调用 当一个方法被throws修饰时,调用方法时必须考虑异常捕捉问题。
throw: 声明位置 方法执行体
1 2 3 public void test1 () {throw new RuntimeException ();}
作用 throw是一个命令,执行时抛出一个指定异常对象。
携带数据: throw后面携带【异常对象】,一个throw一次只能携带一个异常对。
调用 当一个方法内部存在throw命令时,在调用时可以不考虑异常捕捉问题。
查看异常的追踪信息 异常对象的两个方法:
1 2 String msg = e.getMessage(); e.printStackTrace();
异常信息追踪信息,从上往下一行一行看。 但是需要注意的是:SUN写的代码就不用看了(看包名)。 主要的问题出现在自己编写的代码上。
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 import java.io.FileInputStream;import java.io.FileNotFoundException;public class ExceptionTest09 { public static void main (String[] args) { try { m1(); } catch (FileNotFoundException e) { String msg = e.getMessage(); System.out.println(msg); e.printStackTrace(); } System.out.println("Hello World!" ); } private static void m1 () throws FileNotFoundException { m2(); } private static void m2 () throws FileNotFoundException { m3(); } private static void m3 () throws FileNotFoundException { new FileInputStream("C:\\jetns-agent.jar" ); } }
finally 关于try..catch中的finally子句:
在finally子句中的代码是最后执行的,未退出JVM或违反基本规则的前提下,finally子句一定会执行,即使try语句块中的代码出现了异常。
finally子句必须和try一起出现,可以没有catch,不能单独编写。
使用例:
例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class ExceptionTest11 { public static void main (String[] args) { 以下代码的执行顺序: 先执行try ... 再执行finally ... 最后执行 return (return 语句只要执行方法必然结束。) try { System.out.println("try..." ); return ; } finally { System.out.println("finally..." ); } 这里不能写语句,因为这个代码是无法执行到的。 } }
退出JVM
1 2 3 4 5 6 7 8 9 10 11 public class ExceptionTest12 { public static void main (String[] args) { try { System.out.println("try..." ); System.exit(0 ); } finally { System.out.println("finally..." ); } } }
finally面试题
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 public class ExceptionTest13 { public static void main (String[] args) { int result = m(); System.out.println(result); } java语法规则(有一些规则不能破坏): 方法体中的代码必须遵循自上而下顺序依次逐行执行。 return 语句一旦执行,整个方法必须结束。 public static int m () { int i = 100 ; try { return i; } finally { i++; } } 反编译之后的效果 public static int m () { int i = 100 ; int j = i; i++; return j; } }
final finally finalize的区别
final:
final修饰的类无法继承
final修饰的方法无法覆盖
final修饰的变量不能重新赋值。
finally:
和try一起联合使用,finally语句块中的代码是必须执行的。
finalize:(过时)
是一个Object类中的方法名,这个方法是由垃圾回收器GC负责调用的。
自定义异常 两步:
第一步:编写一个类继承Exception或者RuntimeException.
第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void push (Object obj) throws MyStackOperationException { if (index >= elements.length - 1 ){ System.out.println("压栈失败,栈已满!" ); return ; throw new MyStackOperationException("压栈失败,栈已满!" ); } }
1 2 3 4 5 6 7 8 public class MyStackOperationException extends Exception { public MyStackOperationException () { } public MyStackOperationException (String s) { super (s); } }
集合
集合是一个容器。可以来容纳其它类型的数据。
集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,
集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
所有的集合类和集合接口都在java.util包下。
注意: 集合在java中本身是一个容器,是一个对象。 集合中任何时候存储的都是“引用”。
集合分为两大类: 单个方式存储元素: 单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
以键值对的方式存储元素 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
Collection接口 1、Collection中能存放什么元素? 没有使用“泛型”之前,Collection中可以存储Object的所有子类型。 使用了“泛型”之后,Collection中只能存储某个具体的类型。 2、Collection中的常用方法
1 2 3 4 5 6 7 boolean add (Object e) 向集合中添加元素int size () 获取集合中元素的个数void clear () 清空集合boolean contains (Object o) 判断当前集合中是否包含元素o,包含返回true ,不包含返回false boolean remove (Object o) 删除集合中的某个元素。boolean isEmpty () 判断该集合中元素的个数是否为0Object[] toArray () 调用这个方法可以把集合转换成数组。【作为了解,使用不多。】
使用例:
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 import java.util.ArrayList;import java.util.Collection;public class CollectionTest01 { public static void main (String[] args) { Collection c = new ArrayList(); c.add(1200 ); c.add(3.14 ); c.add(new Object()); c.add(new Student()); c.add(true ); System.out.println("集合中元素个数是:" + c.size()); c.clear(); System.out.println("集合中元素个数是:" + c.size()); c.add("hello" ); c.add("world" ); c.add("浩克" ); c.add("绿巨人" ); c.add(1 ); boolean flag = c.contains("绿巨人" ); System.out.println(flag); boolean flag2 = c.contains("绿巨人2" ); System.out.println(flag2); System.out.println(c.contains(1 )); System.out.println("集合中元素个数是:" + c.size()); c.remove(1 ); System.out.println("集合中元素个数是:" + c.size()); System.out.println(c.isEmpty()); c.clear(); System.out.println(c.isEmpty()); c.add("abc" ); c.add("def" ); c.add(100 ); c.add("helloworld!" ); c.add(new Student()); Object[] objs = c.toArray(); for (int i = 0 ; i < objs.length; i++){ Object o = objs[i]; System.out.println(o); } } } class Student {}
集合遍历/迭代 注意 :以下讲解的遍历方式/迭代方式,在所有的Collection以及子类中使用,不能用在Map集合中。
以下两个方法是迭代器对象Iterator中的方法:
1 2 boolean hasNext () 如果仍有元素可以迭代,则返回 true 。Object next () 返回迭代的下一个元素。
使用例:
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 import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;public class CollectionTest02 { public static void main (String[] args) { Collection c1 = new ArrayList(); c1.add(1 ); c1.add(2 ); c1.add(3 ); c1.add(4 ); c1.add(1 ); Iterator it = c1.iterator(); while (it.hasNext()){ Object obj = it.next(); if (obj instanceof Integer){ System.out.println("Integer类型" ); } System.out.println(obj); } Collection c2 = new HashSet(); c2.add(100 ); c2.add(200 ); c2.add(300 ); c2.add(90 ); c2.add(400 ); c2.add(50 ); c2.add(60 ); c2.add(100 ); Iterator it2 = c2.iterator(); while (it2.hasNext()){ System.out.println(it2.next()); } } }
注意:
Collection集合的contains方法和remove方法都调用了equals方法进行比对。equals方法返回true,就表示包含这个元素。因此存放在一个集合中的类型,一定要重写equals方法。
关于集合元素的remove 重点:
当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现 异常:java.util.ConcurrentModificationException
在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素:
c.remove(o); 迭代过程中禁止这样写。
会出现:java.util.ConcurrentModificationException
在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素, 不要使用集合自带的remove方法删除元素。
1 2 3 4 5 6 7 8 9 while (it2.hasNext()){ Object o = it2.next(); Collection c2 = new ArrayList(); c2.remove(o); Iterator it2 = c2.iterator(); it2.remove(); }
List接口
List接口特色常用方法 1 2 3 4 5 6 void add (int index, Object element) Object set (int index, Object element) Object get (int index) int indexOf (Object o) int lastIndexOf (Object o) Object remove (int index)
使用例:
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 import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedList;import java.util.List;public class ListTest01 { public static void main (String[] args) { List myList = new ArrayList(); myList.add("A" ); myList.add("B" ); myList.add("C" ); myList.add("C" ); myList.add("D" ); myList.add(1 , "KING" ); Iterator it = myList.iterator(); while (it.hasNext()){ Object elt = it.next(); System.out.println(elt); } Object firstObj = myList.get(0 ); System.out.println(firstObj); for (int i = 0 ; i < myList.size(); i++){ Object obj = myList.get(i); System.out.println(obj); } System.out.println(myList.indexOf("C" )); System.out.println(myList.lastIndexOf("C" )); myList.remove(0 ); System.out.println(myList.size()); System.out.println("====================================" ); myList.set(2 , "Soft" ); } }
ArrayList集合 构造方法: 1 2 new ArrayList();new ArrayList(20 );
使用例:
1 2 3 4 5 6 Collection c = new HashSet(); List myList3 = new ArrayList(c); for (int i = 0 ; i < myList3.size(); i++){ System.out.println(myList3.get(i));
特点:
默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)
集合底层是一个Object[]数组。
ArrayList集合的扩容:增长到原容量的1.5倍。
ArrayList集合是非线程安全的。
ArrayList集合底层是数组,所以应尽可能少的扩容。
面试官经常问的一个问题? 这么多的集合中,你用哪个集合最多? 答:ArrayList集合。 因为往数组末尾添加元素,效率不受影响。 另外,我们检索/查找某个元素的操作比较多。
LinkedList集合 LinkedList底层使用双向链表,随机增删效率较高,检索/查找的效率较低。
使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 List list2 = new ArrayList(); List list2 = new LinkedList(); list2.add("123" ); list2.add("456" ); list2.add("789" ); for (int i = 0 ; i < list2.size(); i++){ System.out.println(list2.get(i)); }
Vector 特点:
底层也是一个数组。
初始化容量:10
扩容之后是原容量的2倍。
Vector中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的。效率较低,使用较少。
扩展:将一个线程不安全的ArrayList集合转换成线程安全的
使用集合工具类: java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections 是集合工具类。
1 2 List myList = new ArrayList(); Collections.synchronizedList(myList);
foreach JDK5.0之后推出的新特性:叫做增强for循环,或者叫做foreach
缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。
语法:
1 2 3 for (元素类型 变量名 : 数组或集合){ System.out.println(变量名); }
使用例:
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 ForEachTest02 { public static void main (String[] args) { List<String> strList = new ArrayList<>(); strList.add("hello" ); strList.add("world!" ); strList.add("kitty!" ); Iterator<String> it = strList.iterator(); while (it.hasNext()){ String s = it.next(); System.out.println(s); } for (int i = 0 ; i < strList.size(); i++){ System.out.println(strList.get(i)); } for (String s : strList){ System.out.println(s); } List<Integer> list = new ArrayList<>(); list.add(100 ); list.add(200 ); list.add(300 ); for (Integer i : list){ System.out.println(i); } } }
泛型
JDK5.0之后推出的新特性。
泛型这种语法机制,只在程序编译阶段起作用,给编译器作参考。
优点
集合中存储的元素类型统一了。
从集合中取出的元素类型是泛型指定的类型,不需要进行向下转型。
缺点 导致集合中存储的元素缺乏多样性。
自动类型推断机制 JDK8之后引入了自动类型推断机制(又称钻石表达式)。
使用例:
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 public class GenericTest02 { public static void main (String[] args) { List<String> strList = new ArrayList<>(); strList.add("http://www.126.com" ); strList.add("http://www.baidu.com" ); strList.add("http://www.bjpowernode.com" ); Iterator<String> it2 = strList.iterator(); while (it2.hasNext()){ String s = it2.next(); String newString = s.substring(7 ); System.out.println(newString); } } }
自定义泛型 自定义泛型的时候,<> 尖括号中的标识符可自定义。在java源代码中经常使用 和,E是Element单词首字母,T是Type单词首字母。
使用例:
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 public class GenericTest03 <标识符随便写> { public void doSome (标识符随便写 o) { System.out.println(o); } public static void main (String[] args) { GenericTest03<String> gt = new GenericTest03<>(); gt.doSome("abc" ); MyIterator<String> mi = new MyIterator<>(); String s1 = mi.get(); } } class MyIterator <T > { public T get () { return null ; } }
Map接口 Map和Collection没有继承关系。 Map集合以key和value的方式存储数据:键值对 key和value都是引用数据类型。 key和value都是存储对象的内存地址。 key起主导作用,value附属。
常用方法: 1 2 3 4 5 6 7 8 9 10 11 V put (K key, V value) 向Map集合中添加键值对 V get (Object key) 通过key获取value void clear () 清空Map集合 boolean containsKey (Object key) 判断Map中是否包含某个key boolean containsValue (Object value) 判断Map中是否包含某个value boolean isEmpty () 判断Map集合中元素个数是否为0 V remove (Object key) 通过key删除键值对 int size () 获取Map集合中键值对的个数。 Collection<V> values () 获取Map集合中所有的value,返回一个Collection Set<K> keySet () 获取Map集合所有的key(所有的键是一个set集合) Set<Map.Entry<K,V>> entrySet () 将Map集合转换成Set集合
使用例:
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 public class MapTest01 { public static void main (String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(1 , "zhangsan" ); map.put(2 , "lisi" ); map.put(3 , "wangwu" ); map.put(4 , "zhaoliu" ); String value = map.get(2 ); System.out.println(value); System.out.println("键值对的数量:" + map.size()); map.remove(2 ); System.out.println("键值对的数量:" + map.size()); System.out.println(map.containsKey(new Integer(4 ))); System.out.println(map.containsValue(new String("wangwu" ))); Collection<String> values = map.values(); for (String s : values){ System.out.println(s); } map.clear(); System.out.println("键值对的数量:" + map.size()); System.out.println(map.isEmpty()); } }
Map集合的遍历 第一种方式:获取所有的key,通过遍历key,来遍历value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Map<Integer, String> map = new HashMap<>(); map.put(1 , "zhangsan" ); map.put(2 , "lisi" ); map.put(3 , "wangwu" ); map.put(4 , "zhaoliu" ); Set<Integer> keys = map.keySet(); for (Integer key : keys){ System.out.println(key + "=" + map.get(key)); }
第二种方式:Set<Map.Entry<K,V>> entrySet()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Set<Map.Entry<Integer,String>> set = map.entrySet(); for (Map.Entry<Integer,String> node : set){ System.out.println(node.getKey() + "--->" + node.getValue()); }
HashMap集合
HashSet集合 无序不可重复。
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 public class HashSetTest01 { public static void main (String[] args) { Set<String> strs = new HashSet<>(); strs.add("hello3" ); strs.add("hello4" ); strs.add("hello1" ); strs.add("hello2" ); strs.add("hello3" ); strs.add("hello3" ); strs.add("hello3" ); strs.add("hello3" ); for (String s : strs){ System.out.println(s); } } }
HashMap性质:
HashMap集合底层是哈希表/散列表的数据结构。
哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。
HashMap集合的默认初始化容量是16,默认加载因子是0.75:当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。
向Map集合中存或取,都先调用key的hashCode方法,然后再调用equals方法。当数组下标位置上是null时,equals方法不调用。
HashMap集合key部分允许null,但只能有一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class HashMapTest03 { public static void main (String[] args) { Map map = new HashMap(); map.put(null , null ); System.out.println(map.size()); map.put(null , 100 ); System.out.println(map.size()); System.out.println(map.get(null )); } }
重点: HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是为了达到散列均匀,提高HashMap集合的存取效率。
HashMap集合底层源代码: 1 2 3 4 5 6 7 8 9 10 11 public class HashMap { Node<K,V>[] table; static class Node <K ,V > { final int hash; final K key; V value; Node<K,V> next; } }
掌握这两个方法的实现原理。
1 2 map.put(k,v) v = map.get(k)
HashMap集合的key部分特点:
无序:不一定挂到哪个单向链表上。
不可重复: equals方法来保证HashMap集合的key不可重复,如果key重复了,value会覆盖。
注意: 放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
哈希表HashMap使用不当时无法发挥性能:
hashCode()方法返回值固定为某个值:底层哈希表变成了纯单向链表。这种情况为散列分布不均匀。 hashCode()方法返回值都设定为不一样的值:底层哈希表成为一维数组,也是散列分布不均匀。 散列分布均匀需要重写hashCode()方法时有一定的技巧。
Hashtable
Hashtable和HashMap一样,底层都是哈希表数据结构。
初始化容量是11,默认加载因子是:0.75f。
扩容:原容量 * 2 + 1。
Hashtable的key和value都不能为null。
Hashtable方法都带有synchronized:线程安全的。线程安全有其它的方案,Hashtable对线程的处理效率较低,使用较少。
Properties
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
Properties被称为属性类对象。是线程安全的。
使用例:
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 PropertiesTest01 { public static void main (String[] args) { Properties pro = new Properties(); pro.setProperty("url" , "jdbc:mysql://localhost:3306/bjpowernode" ); pro.setProperty("driver" ,"com.mysql.jdbc.Driver" ); pro.setProperty("username" , "root" ); pro.setProperty("password" , "123" ); String url = pro.getProperty("url" ); String driver = pro.getProperty("driver" ); String username = pro.getProperty("username" ); String password = pro.getProperty("password" ); System.out.println(url); System.out.println(driver); System.out.println(username); System.out.println(password); } }
比较接口 TreeSet
TreeSet集合底层是TreeMap,TreeMap集合底层是一个二叉树。
放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合。
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 public class TreeSetTest02 { public static void main (String[] args) { TreeSet<String> ts = new TreeSet<>(); ts.add("zhangsan" ); ts.add("lisi" ); ts.add("wangwu" ); ts.add("zhangsi" ); ts.add("wangliu" ); for (String s : ts){ System.out.println(s); } TreeSet<Integer> ts2 = new TreeSet<>(); ts2.add(100 ); ts2.add(200 ); ts2.add(900 ); ts2.add(800 ); ts2.add(600 ); ts2.add(10 ); for (Integer elt : ts2){ System.out.println(elt); } } }
实现自定义类型比较 放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
方式一:实现java.lang.Comparable接口,并且实现compareTo方法。
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 import java.util.TreeSet;public class TreeSetTest05 { public static void main (String[] args) { TreeSet<Vip> vips = new TreeSet<>(); vips.add(new Vip("zhangsi" , 20 )); vips.add(new Vip("zhangsan" , 20 )); vips.add(new Vip("king" , 18 )); vips.add(new Vip("soft" , 17 )); for (Vip vip : vips){ System.out.println(vip); } } } class Vip implements Comparable <Vip > { String name; int age; public Vip (String name, int age) { this .name = name; this .age = age; } @Override public String toString () { return "Vip{" + "name='" + name + '\'' + ", age=" + age + '}' ; } @Override public int compareTo (Vip v) { if (this .age == v.age){ return this .name.compareTo(v.name); } else { return this .age - v.age; } } }
方式二:使用比较器
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.Comparator;import java.util.TreeSet;public class TreeSetTest06 { public static void main (String[] args) { TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator()); wuGuis.add(new WuGui(1000 )); wuGuis.add(new WuGui(800 )); wuGuis.add(new WuGui(810 )); for (WuGui wuGui : wuGuis){ System.out.println(wuGui); } } } class WuGui { int age; public WuGui (int age) { this .age = age; } @Override public String toString () { return "小乌龟[" + "age=" + age + ']' ; } } class WuGuiComparator implements Comparator <WuGui > { @Override public int compare (WuGui o1, WuGui o2) { return o1.age - o2.age; } }
区分:
集合工具类 使用例:
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 import java.util.*;public class CollectionsTest { public static void main (String[] args) { List<String> list = new ArrayList<>(); Collections.synchronizedList(list); list.add("abf" ); list.add("abx" ); list.add("abc" ); list.add("abe" ); Collections.sort(list); for (String s : list){ System.out.println(s); } List<WuGui2> wuGuis = new ArrayList<>(); wuGuis.add(new WuGui2(1000 )); wuGuis.add(new WuGui2(8000 )); wuGuis.add(new WuGui2(500 )); Collections.sort(wuGuis); for (WuGui2 wg : wuGuis){ System.out.println(wg); } Set<String> set = new HashSet<>(); set.add("king" ); set.add("kingsoft" ); set.add("king2" ); set.add("king1" ); List<String> myList = new ArrayList<>(set); Collections.sort(myList); for (String s : myList) { System.out.println(s); } } } class WuGui2 implements Comparable <WuGui2 > { int age; public WuGui2 (int age) { this .age = age; } @Override public int compareTo (WuGui2 o) { return this .age - o.age; } @Override public String toString () { return "WuGui2{" + "age=" + age + '}' ; } }
IO流 IO流按照读取数据方式不同进行分类:
java中所有的流都是在:java.io.*;下。
在java中“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
四大类:
1 2 3 4 java.io.InputStream 字节输入流 java.io.OutputStream 字节输出流 java.io.Reader 字符输入流 java.io.Writer 字符输出流
特点:
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。
所有的输出流都实现了:java.io.Flushable接口,都有flush()方法。
输出流在最终输出之后,一定要记得flush()刷新。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完。刷新的作用就是清空管道。 注意: 如果没有flush()可能会导致丢失数据。
java.io包下需要掌握的16个流: 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 文件专属: java.io.FileInputStream(掌握) java.io.FileOutputStream(掌握) java.io.FileReader java.io.FileWriter 转换流:(将字节流转换成字符流) java.io.InputStreamReader java.io.OutputStreamWriter 缓冲流专属: java.io.BufferedReader java.io.BufferedWriter java.io.BufferedInputStream java.io.BufferedOutputStream 数据流专属: java.io.DataInputStream java.io.DataOutputStream 标准输出流: java.io.PrintWriter java.io.PrintStream(掌握) 对象专属流: java.io.ObjectInputStream(掌握) java.io.ObjectOutputStream(掌握)
文件字节输入流,任何类型的文件都可以采用这个流来读。
字节的方式,完成输入的操作,完成读的操作(硬盘—> 内存)
常用方法: 返回值是读取到的“字节”本身:
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 import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class FileInputStreamTest04 { public static void main (String[] args) { FileInputStream fis = null ; try { fis = new FileInputStream("chapter23/src/tempfile3" ); byte [] bytes = new byte [4 ]; int readCount = 0 ; while ((readCount = fis.read(bytes)) != -1 ) { System.out.print(new String(bytes, 0 , readCount)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null ) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
返回流当中剩余的没有读到的字节数量:
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 import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class FileOutputStreamTest01 { public static void main (String[] args) { FileOutputStream fos = null ; try { fos = new FileOutputStream("chapter23/src/tempfile3" , true ); byte [] bytes = {97 , 98 , 99 , 100 }; fos.write(bytes); fos.write(bytes, 0 , 2 ); String s = "我是一个中国人,我骄傲!!!" ; byte [] bs = s.getBytes(); fos.write(bs); fos.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fos != null ) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
FileReader和FileWriter 类似,只能拷贝“普通文本”文件。
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 import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class Copy02 { public static void main (String[] args) { FileReader in = null ; FileWriter out = null ; try { in = new FileReader("java/io/Copy02.java" ); out = new FileWriter("Copy02.java" ); char [] chars = new char [1024 * 512 ]; int readCount = 0 ; while ((readCount = in.read(chars)) != -1 ){ out.write(chars, 0 , readCount); } out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null ) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null ) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
BufferedReader 带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,byte数组。自带缓冲。
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 import java.io.BufferedReader;import java.io.FileInputStream;import java.io.InputStream;import java.io.InputStreamReader;public class BufferedReaderTest02 { public static void main (String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Copy02.java" ))); String line = null ; while ((line = br.readLine()) != null ){ System.out.println(line); } br.close(); } }
BufferedWriter: 带有缓冲的字符输出流。
OutputStreamWriter:转换流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.io.BufferedWriter;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.OutputStreamWriter;public class BufferedWriterTest { public static void main (String[] args) throws Exception { BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("copy" , true ))); out.write("hello world!" ); out.write("\n" ); out.write("hello kitty!" ); out.flush(); out.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 import java.io.DataInputStream;import java.io.FileInputStream;import java.io.DataOutputStream;import java.io.FileOutputStream;public class DataInputStreamTest01 { public static void main (String[] args) throws Exception { DataOutputStream dos = new DataOutputStream(new FileOutputStream("data" )); byte b = 100 ; short s = 200 ; int i = 300 ; long l = 400L ; dos.writeByte(b); dos.writeShort(s); dos.writeInt(i); dos.writeLong(l); dos.flush(); dos.close(); DataInputStream dis = new DataInputStream(new FileInputStream("data" )); byte b = dis.readByte(); short s = dis.readShort(); int i = dis.readInt(); long l = dis.readLong(); System.out.println(b); System.out.println(s); System.out.println(i); System.out.println(l); dis.close(); } }
PrintStream 标准的字节输出流。默认输出到控制台。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class PrintStreamTest { public static void main (String[] args) throws Exception { System.out.println("hello world!" ); PrintStream ps = System.out; ps.println("hello world!" ); PrintStream printStream = new PrintStream(new FileOutputStream("log" )); System.setOut(printStream); System.out.println("hello world" ); System.out.println("hello kitty" ); System.out.println("hello zhangsan" ); } }
应用:日志文件生成
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 import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.PrintStream;import java.text.SimpleDateFormat;import java.util.Date;public class LogTest { public static void main (String[] args) { Logger.log("调用了System类的gc()方法,建议启动垃圾回收" ); Logger.log("调用了UserService的doSome()方法" ); Logger.log("用户尝试进行登录,验证失败" ); } } public class Logger { public static void log (String msg) { try { PrintStream out = new PrintStream(new FileOutputStream("log.txt" , true )); System.setOut(out); Date nowTime = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); String strTime = sdf.format(nowTime); System.out.println(strTime + ": " + msg); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
序列化 java语言中是采用什么机制来区分类的? 第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。 第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。
结论
参与序列化和反序列化的对象,必须实现Serializable接口。
实现Serializable接口起到标识的作用,会为该类自动生成一个序列化版本号。
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,即使以后这个类代码修改了,但版本号不变,java虚拟机会认为是同一个类。
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 import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.util.ArrayList;import java.util.List;public class ObjectOutputStreamTest02 { public static void main (String[] args) throws Exception { List<User> userList = new ArrayList<>(); userList.add(new User(1 ,"zhangsan" )); userList.add(new User(2 , "lisi" )); userList.add(new User(3 , "wangwu" )); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users" )); oos.writeObject(userList); oos.flush(); oos.close(); } } public class ObjectInputStreamTest02 { public static void main (String[] args) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users" )); List<User> userList = (List<User>)ois.readObject(); for (User user : userList){ System.out.println(user); } ois.close(); } }
transient 关键字,表示不参与序列化。
1 private transient String name;
IO+Properties的联合应用 经常改变的数据,可以单独写到一个文件中,使用程序动态读取。 类似于以上机制的这种文件被称为配置文件。并且当配置文件中的内容格式是:
我们把这种配置文件叫做属性配置文件。属性配置文件建议以.properties结尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class IoPropertiesTest01 { public static void main (String[] args) throws Exception { FileReader reader = new FileReader("chapter23/userinfo.properties" ); Properties pro = new Properties(); pro.load(reader); String username = pro.getProperty("username" ); System.out.println(username); String password = pro.getProperty("password" ); System.out.println(password); String data = pro.getProperty("data" ); System.out.println(data); } }
File类 File是一个路径名的抽象表示形式。一个File对象有可能对应的是目录,也可能是文件。
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 38 import java.io.File;public class FileTest01 { public static void main (String[] args) throws Exception { File f1 = new File("D:\\file" ); System.out.println(f1.exists()); File f2 = new File("D:/a/b/c/d/e/f" ); File f3 = new File("D:\\course\\01-开课\\学习方法.txt" ); String parentPath = f3.getParent(); System.out.println(parentPath); File parentFile = f3.getParentFile(); System.out.println("获取绝对路径:" + parentFile.getAbsolutePath()); File f4 = new File("copy" ); System.out.println("绝对路径:" + f4.getAbsolutePath()); } }
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 FileTest02 { public static void main (String[] args) { File f1 = new File("D:\\course\\01-开课\\开学典礼.ppt" ); System.out.println("文件名:" + f1.getName()); System.out.println(f1.isDirectory()); System.out.println(f1.isFile()); long haoMiao = f1.lastModified(); Date time = new Date(haoMiao); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); String strTime = sdf.format(time); System.out.println(strTime); System.out.println(f1.length()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class FileTest03 { public static void main (String[] args) { File f = new File("D:\\course\\01-开课" ); File[] files = f.listFiles(); for (File file : files){ System.out.println(file.getName()); } } }
多线程
实现线程 java语言中,实现线程有三种方式:
第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
1 2 3 4 5 6 7 8 9 10 public class MyThread extends Thread { public void run () { } } MyThread t = new MyThread(); t.start();
第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
1 2 3 4 5 6 7 8 9 10 public class MyRunnable implements Runnable { public void run () { } } Thread t = new Thread(new MyRunnable()); t.start();
采用匿名内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ThreadTest04 { public static void main (String[] args) { Thread t = new Thread(new Runnable(){ @Override public void run () { for (int i = 0 ; i < 100 ; i++){ System.out.println("t线程---> " + i); } } }); t.start(); for (int i = 0 ; i < 100 ; i++){ System.out.println("main线程---> " + i); } } }
第二种方式:实现Callable接口。(JDK8新特性。)
优点:可以获取到线程的执行结果。 缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。
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 import java.util.concurrent.Callable;import java.util.concurrent.FutureTask; public class ThreadTest15 { public static void main (String[] args) throws Exception { FutureTask task = new FutureTask(new Callable() { @Override public Object call () throws Exception { System.out.println("call method begin" ); Thread.sleep(1000 * 10 ); System.out.println("call method end!" ); int a = 100 ; int b = 200 ; return a + b; } }); Thread t = new Thread(task); t.start(); Object obj = task.get(); System.out.println("线程执行结果:" + obj); System.out.println("hello world!" ); } }
线程对象的生命周期 新建状态 就绪状态 运行状态 阻塞状态 死亡状态
线程常用方法
1 Thread t = Thread.currentThread();
1 String name = 线程对象.getName();
1 Thread.sleep(long millis);
静态方法,作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。这行代码出现在A线程中,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 31 32 33 34 35 36 37 38 39 40 41 42 public class ThreadTest08 { public static void main (String[] args) { Thread t = new Thread(new MyRunnable2()); t.setName("t" ); t.start(); try { Thread.sleep(1000 * 5 ); } catch (InterruptedException e) { e.printStackTrace(); } t.interrupt(); } } class MyRunnable2 implements Runnable { @Override public void run () { System.out.println(Thread.currentThread().getName() + "---> begin" ); try { Thread.sleep(1000 * 60 * 60 * 24 * 365 ); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "---> end" ); } }
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 public class ThreadTest10 { public static void main (String[] args) { MyRunable4 r = new MyRunable4(); Thread t = new Thread(r); t.setName("t" ); t.start(); try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); } r.run = false ; } } class MyRunable4 implements Runnable { boolean run = true ; @Override public void run () { for (int i = 0 ; i < 10 ; i++){ if (run){ System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } }else { return ; } } } }
线程的调度 常见的线程调度模型?
线程调度方法(理解)
1 2 void setPriority (int newPriority) 设置线程的优先级int getPriority () 获取线程优先级
最低优先级1,默认优先级是5,最高优先级10
暂停当前正在执行的线程对象,并执行其他线程。yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。但在回到就绪之后,有可能还会再次抢到。
1 2 3 4 5 6 7 8 9 10 void join () 合并线程 class MyThread1 extends Thread { public void doSome () { MyThread2 t = new MyThread2(); t.join(); } } class MyThread2 extends Thread {}
线程安全 存在线程安全问题的三个条件: 条件1:多线程并发。 条件2:有共享数据。 条件3:共享数据有修改的行为。
解决线程安全问题:
使用“线程同步机制”让线程排队执行(不能并发)。
区分异步同步
synchronized三种写法 第一种:同步代码块
1 2 3 synchronized (线程共享对象){ 同步代码块; }
第二种:在实例方法上使用synchronized
表示共享对象一定是this
并且同步代码块是整个方法体。
第三种:在静态方法上使用synchronized
表示找类锁。
类锁永远只有1把。
就算创建了100个对象,那类锁也只有一把。
变量线程安全 实例变量:在堆中。 静态变量:在方法区。 局部变量:在栈中。
以上三大变量中:
局部变量,常量:不会有线程安全问题。 成员变量:可能会有线程安全问题。
死锁 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 public class DeadLock { public static void main (String[] args) { Object o1 = new Object(); Object o2 = new Object(); Thread t1 = new MyThread1(o1,o2); Thread t2 = new MyThread2(o1,o2); t1.start(); t2.start(); } } class MyThread1 extends Thread { Object o1; Object o2; public MyThread1 (Object o1,Object o2) { this .o1 = o1; this .o2 = o2; } public void run () { synchronized (o1){ try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ } } } } class MyThread2 extends Thread { Object o1; Object o2; public MyThread2 (Object o1,Object o2) { this .o1 = o1; this .o2 = o2; } public void run () { synchronized (o2){ try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ } } } }
守护线程 java语言中线程分为两大类: 一类是:用户线程 一类是:守护线程(后台线程) 其中具有代表性的就是:垃圾回收线程(守护线程)。
守护线程的特点: 一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束。
注意:主线程main方法是一个用户线程。
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 public class ThreadTest14 { public static void main (String[] args) { Thread t = new BakDataThread(); t.setName("备份数据的线程" ); t.setDaemon(true ); t.start(); for (int i = 0 ; i < 10 ; i++){ System.out.println(Thread.currentThread().getName() + "--->" + i); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } } class BakDataThread extends Thread { public void run () { int i = 0 ; while (true ){ System.out.println(Thread.currentThread().getName() + "--->" + (++i)); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
定时器 定时器的作用: 间隔特定的时间,执行特定的程序。
在java中其实可以采用多种方式实现:
使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行
任务。这种方式是最原始的定时器。(比较low)
在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持定时任务的。
在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架,这个框架只要进行简单的配置,就可以完成定时器的任务。
wait和notify方法
wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方式是Object类中自带的。
wait方法和notify方法不通过线程对象调用:t.wait(),t.notify()..错误。
wait方法和notify方法建立在线程同步的基础之上。
wait()方法
1 2 Object o = new Object(); o.wait();
表示: 让正在o对象上活动的线程进入等待状态,直到被唤醒为止。 o.wait();方法的调用,会让“当前线程(正在o对象上 活动的线程)”进入等待状态,并且释放掉线程之前占有的o对象的锁。
1 2 Object o = new Object(); o.notify();
表示: 唤醒正在o对象上等待的线程,不会释放o对象上之前占有的锁。
notifyAll()方法:唤醒o对象上处于等待的所有线程X。
使用wait方法和notify方法实现“生产者和消费者模式” 生产线程负责生产,消费线程负责消费。 生产线程和消费线程要达到均衡。 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。
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 import java.util.ArrayList;import java.util.List;public class ThreadTest16 { public static void main (String[] args) { List list = new ArrayList(); Thread t1 = new Thread(new Producer(list)); Thread t2 = new Thread(new Consumer(list)); t1.setName("生产者线程" ); t2.setName("消费者线程" ); t1.start(); t2.start(); } } class Producer implements Runnable { private List list; public Producer (List list) { this .list = list; } @Override public void run () { while (true ){ synchronized (list){ if (list.size() > 0 ){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object obj = new Object(); list.add(obj); System.out.println(Thread.currentThread().getName() + "--->" + obj); list.notifyAll(); } } } } class Consumer implements Runnable { private List list; public Consumer (List list) { this .list = list; } @Override public void run () { while (true ){ synchronized (list) { if (list.size() == 0 ){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object obj = list.remove(0 ); System.out.println(Thread.currentThread().getName() + "--->" + obj); list.notifyAll(); } } } }
反射机制 通过java语言中的反射机制可以操作字节码文件。可以读和修改字节码文件。通过反射机制可以操作代码片段。(class文件。)
反射机制的相关类在java.lang.reflect.*;包下。
获取类的字节码 三种方式:
1 2 3 第一种:Class c = Class.forName("完整类名带包名" ); 第二种:Class c = 对象.getClass(); 第三种:Class c = 任何类型.class;
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 import java.util.Date;public class ReflectTest01 { public static void main (String[] args) { Class c1 = null ; Class c2 = null ; try { c1 = Class.forName("java.lang.String" ); c2 = Class.forName("java.util.Date" ); Class c3 = Class.forName("java.lang.Integer" ); Class c4 = Class.forName("java.lang.System" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } String s = "abc" ; Class x = s.getClass(); System.out.println(c1 == x); Date time = new Date(); Class y = time.getClass(); System.out.println(c2 == y); Class z = String.class; Class k = Date.class; Class f = int .class; Class e = double .class; System.out.println(x == z); } }
通过Class的newInstance()方法来实例化对象。 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 public class ReflectTest02 { public static void main (String[] args) { User user = new User(); System.out.println(user); try { Class c = Class.forName("com.bjpowernode.java.bean.User" ); Object obj = c.newInstance(); System.out.println(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } } public class User { public User () { System.out.println("无参数构造方法!" ); } public User (String s) { } }
验证反射机制的灵活性 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 import java.io.FileReader;import java.util.Properties;public class ReflectTest03 { public static void main (String[] args) throws Exception { FileReader reader = new FileReader("chapter/classinfo2.properties" ); Properties pro = new Properties(); pro.load(reader); reader.close(); String className = pro.getProperty("className" ); Class c = Class.forName(className); Object obj = c.newInstance(); System.out.println(obj); } }
只加载静态代码块 记住,重点: 如果只希望一个类的静态代码块执行,其它代码一律不执行,可以使用:
这个方法的执行会导致类加载,类加载时,静态代码块执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class ReflectTest04 { public static void main (String[] args) { try { Class.forName("com.java.reflect.MyClass" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } public class MyClass { static { System.out.println("MyClass类的静态代码块执行了!" ); } }
获取文件绝对路径 以下的方式是通用的。但前提是:文件需要在类路径下。
1 2 String Thread.currentThread().getContextClassLoader() .getResource("文件名" ).getPath();
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 import java.io.FileReader;public class AboutPath { public static void main (String[] args) throws Exception { String path = Thread.currentThread().getContextClassLoader() .getResource("classinfo2.properties" ).getPath(); System.out.println(path); String path2 = Thread.currentThread().getContextClassLoader() .getResource("com/java/bean/db.properties" ).getPath(); System.out.println(path2); } }
直接以流的形式返回:
1 2 InputStream = Thread.currentThread().getContextClassLoader() .getResourceAsStream("文件名" );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.io.FileReader;import java.io.InputStream;import java.util.Properties;public class IoPropertiesTest { public static void main (String[] args) throws Exception { InputStream reader = Thread.currentThread().getContextClassLoader() .getResourceAsStream("classinfo2.properties" ); Properties pro = new Properties(); pro.load(reader); reader.close(); String className = pro.getProperty("className" ); System.out.println(className); } }
类 Field 怎么通过反射机制访问一个java对象的属性? 给属性赋值set 获取属性的值get
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 import java.lang.reflect.Field;public class ReflectTest07 { public static void main (String[] args) throws Exception { Student s = new Student(); s.no = 1111 ; System.out.println(s.no); Class studentClass = Class.forName("com.java.bean.Student" ); Object obj = studentClass.newInstance(); Field noFiled = studentClass.getDeclaredField("no" ); noFiled.set(obj, 22222 ); System.out.println(noFiled.get(obj)); Field nameField = studentClass.getDeclaredField("name" ); nameField.setAccessible(true ); nameField.set(obj, "jackson" ); System.out.println(nameField.get(obj)); } } public class Student { private String name; protected int age; boolean sex; public int no; public static final double MATH_PI = 3.1415926 ; }
类 Method 可变长度参数 int… args 这就是可变长度参数 语法是:类型… (注意:一定是3个点。)
可变长度参数要求的参数个数是:0~N个。
可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
可变长度参数可以当做一个数组来看待。
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 ArgsTest { public static void main (String[] args) { m(); m(10 ); m(10 , 20 ); m2(100 ); m2(200 , "abc" ); m2(200 , "abc" , "def" ); m2(200 , "abc" , "def" , "xyz" ); m3("ab" , "de" , "kk" , "ff" ); String[] strs = {"a" ,"b" ,"c" }; m3(strs); m3(new String[]{"我" ,"是" ,"中" ,"国" , "人" }); m3("我" ,"是" ,"中" ,"国" , "人" ); } public static void m (int ... args) { System.out.println("m方法执行了!" ); } public static void m2 (int a, String... args1) { } public static void m3 (String... args) { for (int i = 0 ; i < args.length; i++){ System.out.println(args[i]); } } }
通过反射机制调用对象方法 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 import java.lang.reflect.Method;public class ReflectTest10 { public static void main (String[] args) throws Exception { UserService userService = new UserService(); boolean loginSuccess = userService.login("admin" ,"123" ); System.out.println(loginSuccess ? "登录成功" : "登录失败" ); Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService" ); Object obj = userServiceClass.newInstance(); Method loginMethod = userServiceClass.getDeclaredMethod("login" , String.class, String.class); Object retValue = loginMethod.invoke(obj, "admin" ,"123123" ); System.out.println(retValue); } } public class UserService { public boolean login (String name,String password) { if ("admin" .equals(name) && "123" .equals(password)){ return true ; } return false ; } public void login (int i) { } public void logout () { System.out.println("系统已经安全退出!" ); } }
类 Constructor 通过反射机制调用构造方法实例化java对象(非重点) 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 import java.lang.reflect.Constructor;public class ReflectTest12 { public static void main (String[] args) throws Exception { Vip v1 = new Vip(); Vip v2 = new Vip(110 , "zhangsan" , "2001-10-11" , true ); Class c = Class.forName("com.bjpowernode.java.bean.Vip" ); Object obj = c.newInstance(); System.out.println(obj); Constructor con = c.getDeclaredConstructor(int .class, String.class, String.class,boolean .class); Object newObj = con.newInstance(110 , "jackson" , "1990-10-11" , true ); System.out.println(newObj); Constructor con2 = c.getDeclaredConstructor(); Object newObj2 = con2.newInstance(); System.out.println(newObj2); } } public class Vip { int no; String name; String birth; boolean sex; public Vip () { } public Vip (int no) { this .no = no; } public Vip (int no, String name) { this .no = no; this .name = name; } public Vip (int no, String name, String birth) { this .no = no; this .name = name; this .birth = birth; } public Vip (int no, String name, String birth, boolean sex) { this .no = no; this .name = name; this .birth = birth; this .sex = sex; } @Override public String toString () { return "Vip{" + "no=" + no + ", name='" + name + '\'' + ", birth='" + birth + '\'' + ", sex=" + sex + '}' ; } }
获取父类实现的接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ReflectTest13 { public static void main (String[] args) throws Exception { Class stringClass = Class.forName("java.lang.String" ); Class superClass = stringClass.getSuperclass(); System.out.println(superClass.getName()); Class[] interfaces = stringClass.getInterfaces(); for (Class in : interfaces){ System.out.println(in.getName()); } } }
注解Annotation 注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。
自定义注解: 1 2 [修饰符列表] @interface 注解类型名{ }
使用注解语法格式
注解可以出现在类上、属性上、方法上、变量上等….还可以出现在注解类型上。
注解当中有属性,那么必须给属性赋值。(除非该属性使用default指定了默认值。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyAnnotationTest { @MyAnnotation(name = "zhangsan", color = "红色") public void doSome () { } } public @interface MyAnnotation { String name () ; String color () ; int age () default 25 ; }
如果一个注解的属性的名字是value,并且只有一个属性的话,在使用的时,该属性名可以省略。
1 2 3 4 5 6 7 8 9 10 11 public class MyAnnotationTest { @MyAnnotation(value = "haha") public void doSome () { } @MyAnnotation("haha") public void doOther () { } } public @interface MyAnnotation { String value () ; }
注解当中属性的类型可以是:byte short int long float double boolean char String Class 枚举类型以及以上每一种的数组形式。
反射注解
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 import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;public class ReflectAnnotationTest { public static void main (String[] args) throws Exception { Class c = Class.forName("com.java.annotation5.MyAnnotationTest" ); if (c.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class); String value = myAnnotation.value(); System.out.println(value); } Class stringClass = Class.forName("java.lang.String" ); System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class)); } } @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value () default "北京大兴区" ; } @MyAnnotation("上海浦东区") public class MyAnnotationTest { int i; public MyAnnotationTest () { } @MyAnnotation public void doSome () { int i; } }
需要掌握的JDK内置注解: java.lang包下的注释类型:
不用掌握: SuppressWarnings 指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
元注解 用来标注“注解类型”的“注解”,称为元注解。
常见的元注解: Target Retention
关于Target注解: 这是一个元注解,用来标注“注解类型”的“注解” 这个Target注解用来标注“被标注的注解”可以出现在哪些位置上。
1 2 3 4 5 6 7 8 9 @Target(ElementType.METHOD) :@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
关于Retention注解: 这是一个元注解,用来标注“注解类型”的“注解” 这个Retention注解用来标注“被标注的注解”最终保存在哪里。
1 2 3 @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.RUNTIME)
Retention的源代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public @interface Retention { RetentionPolicy value () ; } RetentionPolicy的源代码: public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation{}