java基础增强(静态导入和自动装箱拆箱、增强for循环、可变参数、枚举、反射、内省(Introspector)、泛型、读取控制台单行或者多行输入)
1. 静态导入和自动装箱拆箱
(1).静态导入: 导入java里面类的静态成员 import static 包名 类名 静态属性/静态方法/*
(2).自动装箱拆箱:
自动装箱:把基本数据类型赋值给对应的包装类(对象)
自动拆箱:把包装类的对象赋值给对应的基本数据类型
1 package com.study.lgs; 2 import static java.lang.System.out; 3 4 import java.util.ArrayList; 5 import java.util.Iterator; 6 import java.util.List; 7 public class ZhuangXiangChaiXiang { 8 9 public static void main(String[] args) { 10 // TODO Auto-generated method stub 11 //1、静态导入 12 //out.println("aaa"); 13 14 //2.自动装箱拆箱: 15 Integer i=1;//自动装箱 jdk1.5+才能这样写 否则这样写:Integer i=new Integer(1) 16 int j=i;//自动拆箱 17 18 //3.自动装箱拆箱的典型应用 19 List l=new ArrayList(); 20 l.add(1);//装箱 把对象1装到list对象l里面去 21 l.add(2); 22 l.add(3); 23 24 //迭代器遍历输出 25 Iterator k=l.iterator(); 26 while(k.hasNext()){ 27 int j1=(Integer)k.next(); 28 out.println(j1); 29 30 31 } 32 /*ArrayList<Integer> al=new ArrayList<Integer>(); 33 al.add(1); 34 al.add(2); 35 al.add(3); 36 for(Integer k:al){ 37 out.println(k); 38 }*/ 39 } 40 41 }
2. 增强for循环
用途:遍历数组或者遍历实现Iterable泛型接口的集合
1 package cn.study.lgs; 2 import static java.lang.System.out; 3 4 import java.util.ArrayList; 5 import java.util.HashMap; 6 import java.util.Iterator; 7 import java.util.List; 8 import java.util.Map; 9 import java.util.Map.Entry; 10 import java.util.Set; 11 12 import org.junit.Test; 13 14 public class StrongFor { 15 16 @Test 17 public void test1(){ 18 //1、遍历数组 19 int i[]={1,2,3}; 20 for(int k:i){ 21 out.println(k); 22 } 23 } 24 @Test 25 public void test2(){ 26 //2、遍历集合 27 List l=new ArrayList(); 28 l.add(1); 29 l.add(2); 30 l.add(3); 31 // l.add("aaa"); 32 33 for(Object o:l){ 34 // out.println(o); //不用包装直接以对象的形式输出 也可以 35 int i=(Integer) o; 36 out.println(i); 37 } 38 } 39 @Test 40 public void testMap(){ 41 Map m=new HashMap(); 42 m.put("1", "aaa"); 43 m.put("2", "bbb"); 44 m.put("3", "ccc"); 45 //1、传统方案取出Map里面的值 1 46 Set s=m.keySet();//通过keyset方法把map里面的每个key存到s里面去 47 Iterator i=s.iterator();//迭代遍历s里面的key值 48 while(i.hasNext()){ 49 // String key=(String) i.next(); 50 // String value=(String) m.get(key); 51 // out.println(key+"对应的值为:"+value); 52 String st=(String) i.next(); 53 System.out.println(st+ "对应的值为"+m.get(st)); 54 55 // System.out.println(i.next()); //有错 两次的i.next()不一样 56 // System.out.println(m.get(i.next())); 57 58 59 } 60 61 62 } 63 @Test 64 public void testMap2(){ 65 Map m=new HashMap(); 66 m.put("1", "aaa"); 67 m.put("2", "bbb"); 68 m.put("3", "ccc"); 69 //1、传统方案取出Map里面的值 2 70 Set s=m.entrySet();//通过entrySet方法把每组键值存到s中 71 Iterator i=s.iterator();//迭代遍历输出s中的值 72 while(i.hasNext()){ 73 Map.Entry me=(Entry) i.next(); 74 System.out.println(me.getKey()+"对应的值为"+me.getValue()); 75 } 76 77 78 79 } 80 81 @Test 82 public void testMap3(){ 83 Map m=new HashMap(); 84 m.put("1", "aaa"); 85 m.put("2", "bbb"); 86 m.put("3", "ccc"); 87 //1、增强for循环 1 (keySet实现Iterable泛型接口) 88 for(Object o:m.keySet()){ 89 // String s=(String) o; 90 // String value=(String) m.get(o); 91 // System.out.println(o+" "+value); 92 out.println(o+" "+m.get(o)); 93 } 94 95 96 } 97 @Test 98 public void testMap4(){ 99 Map m=new HashMap(); 100 m.put("1", "aaa"); 101 m.put("2", "bbb"); 102 m.put("3", "ccc"); 103 //1、增强for循环 2 (entrySet实现Iterable泛型接口) 104 for(Object o:m.entrySet()){ 105 Map.Entry me=(Entry) o; 106 System.out.println(me.getKey()+" "+me.getValue()); 107 } 108 109 110 } 111 112 @Test //增强for循环应该注意的问题 增强for循环只能取数组或者集合里面的数据 不能更改数据;要改数据的话只能用传统方法 113 public void test3(){ 114 // int []i=new int[]{1,2,3}; 115 // for(int j:i){ 116 // j=10;//j仅仅是指向取出来的值而已 并不能改变数组里面的值 117 // 118 // } 119 // //传统方法改变数组的值 120 // for(int j=0;j<i.length;j++){ 121 // i[j]=10; 122 // } 123 // out.println(i[0]); 124 // out.println(i[1]); 125 // out.println(i[2]); 126 // 127 128 List l=new ArrayList(); 129 l.add("a"); 130 l.add("b"); 131 l.add("c"); 132 for(Object k:l){ 133 k="d";//k仅仅是指向取出来的值而已 并不能改变集合里面的值 134 135 } 136 //传统方案删除集合里面的元素 137 for(int i=0;i<l.size();i++){ 138 139 l.removeAll(l); 140 141 142 143 } 144 l.add("e"); 145 l.add("e"); 146 l.add("e"); 147 System.out.println(l.get(0)); 148 System.out.println(l.get(1)); 149 System.out.println(l.get(2)); 150 151 152 } 153 }
3. 可变参数
使用可变参数的精髓:就是看需要什么类型的数据,需要对象就传入对象/对象数组,需要基本数据类型就传基本数据类型/基本数组
1 package com.study.lgs; 2 import java.util.Arrays; 3 import java.util.List; 4 5 import org.junit.Test; 6 7 public class KeBianCanShu { 8 9 @Test 10 public void testSum(){ 11 int []i={1,2,3}; 12 sum(i);//可变参数里面可以看成一个数组,所以可以向里面传入数组i 13 sum(1,2,3); 14 } 15 //可变参数按数组方式进行处理,只能放在参数列表的最后,一个参数列表中只能有一个可变参数 16 17 public void sum(int...nums){ 18 int sum=0; 19 for(int k:nums){ 20 sum+=k; 21 } 22 System.out.println(sum); 23 } 24 @Test 25 public void testArrays(){ 26 List l=Arrays.asList("1","2","3");//传入普通字符串对象 27 System.out.println(l); 28 29 String [] s={"1","2","3","4"};//传入字符数组对象 30 l=Arrays.asList(s); 31 System.out.println(l); 32 33 //int []i={1,2,3,4,5}; 34 Integer []i={1,2,3,4,5}; 35 l=Arrays.asList(i);//传入基本数据类型整型数组对象 List的l会吧i数组看作一个对象,所以输出的l为一个地址,必须使用包装类的对象以对象方式传入才可输出实际的值 36 System.out.println(l); 37 } 38 39 }
4. 枚举
4.1 枚举的作用和特性
作用:限定某些程序运行时所需要数据的取值范围
特性:
(1) 枚举类也是一种特殊形式的java类
(2) 枚举类中声明的每一个枚举值代表枚举类的一个实例对象
(3) 与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须是私有的(因为枚举类的作用就是限定某些程序运行时所需要数据的取值范围,如果构造函数公有,别人就可以随便更改)
(4) 枚举类也可以实现接口、继承抽象类
(5) jdk5扩展了switch语句,它除了可以接收int,byte,char,short外,还可以接收一个枚举类型
(6) 若枚举类只有一个枚举值,则可以单作单态设计模式使用
1 package enumeration; 2 3 import org.junit.Test; 4 5 //枚举的作用:限定某些程序运行时所需要数据的取值范围 6 enum Grade{ 7 A,B,C,D 8 } 9 public class DefineEnum { 10 @Test 11 public void test(){ 12 DefineEnum.printGrade(Grade.A); 13 } 14 public static void printGrade(Grade a){ 15 switch(a){ 16 case A: 17 System.out.println("90"); 18 break; 19 case B: 20 System.out.println("80"); 21 break; 22 case C: 23 System.out.println("70"); 24 break; 25 case D: 26 System.out.println("60"); 27 break; 28 default: 29 System.out.println("传入参数有错"); 30 31 } 32 } 33 }
4.2 定义枚举的字段、构造函数、方法去封装更多的信息
1 package enumeration; 2 3 import org.junit.Test; 4 5 //枚举的作用:限定某些程序运行时所需要数据的取值范围 6 //怎样定义枚举的字段、构造函数、方法去封装更多的信息 7 enum Grade{ 8 A("100-90"),B("89-80"),C("79-70"),D("69-60");//对枚举进行实例化 9 private String value;//定义每个枚举类型里面存的值 10 private Grade(String value){ 11 this.value=value; 12 } 13 public String getValue(){ //外部对象获取每个枚举类型里面存的值 14 return this.value; 15 } 16 } 17 public class DefineEnum { 18 @Test 19 public void test(){ 20 printGrade(Grade.B); 21 } 22 public void printGrade(Grade a){ 23 System.out.println(a.getValue()); 24 } 25 }
4.3 带抽象方法的枚举 实例化时必须实现抽象方法
1 package enumeration; 2 3 import org.junit.Test; 4 5 //枚举的作用:限定某些程序运行时所需要数据的取值范围 6 //带抽象方法的枚举 实例化时必须实现抽象方法 7 enum Grade{ 8 A("100-90") { 9 @Override 10 public String getLocalValue() { 11 // TODO Auto-generated method stub 12 return "优"; 13 } 14 }, 15 B("89-80") { 16 @Override 17 public String getLocalValue() { 18 // TODO Auto-generated method stub 19 return "良"; 20 } 21 }, 22 C("79-70") { 23 @Override 24 public String getLocalValue() { 25 // TODO Auto-generated method stub 26 return "中"; 27 } 28 }, 29 D("69-60") { 30 @Override 31 public String getLocalValue() { 32 // TODO Auto-generated method stub 33 return "差"; 34 } 35 };//对枚举进行实例化 必须实现抽象方法 36 37 private String value;//定义每个枚举类型里面存的值 38 private Grade(String value){ 39 this.value=value; 40 } 41 public String getValue(){ //外部对象获取每个枚举类型里面存的值 42 return this.value; 43 } 44 //带抽象方法的枚举 45 public abstract String getLocalValue(); 46 } 47 public class DefineEnum { 48 @Test 49 public void test(){ 50 printGrade(Grade.B); 51 } 52 public void printGrade(Grade a){ 53 System.out.println(a.getValue()); 54 System.out.println(a.getLocalValue()); 55 56 } 57 }
4.4 普通单例设计模式与枚举单例设计模式
1 package enumeration; 2 3 //定义普通单态设计模式 4 class Single{ 5 private static Single sin=null; 6 private Single(){} 7 private Single(Single sin){ 8 this.sin=sin; 9 } 10 public static Single createShiLi(){ 11 if(sin==null){ 12 sin=new Single(); 13 } 14 return sin; 15 16 } 17 } 18 //定义枚举类型单态设计模式 当枚举里面只有一个对象时也属于单态设计模式 19 enum EnumSingle{ 20 A;//枚举中声明的每一个枚举值表示枚举类的一个实例化对象 21 //m枚举类的构造函数必须是私有的,因为枚举就是用来限制实例化的对象的,如果构造函数为公有的就可以实例化新的对象,不符合枚举规范 22 } 23 public class DefineSingle { 24 25 public static void main(String[] args) { 26 // TODO Auto-generated method stub 27 //普通设计模式的实例化 28 Single s1=Single.createShiLi(); 29 Single s2=Single.createShiLi(); 30 Single s3=Single.createShiLi(); 31 System.out.println(s1); 32 System.out.println(s2); 33 System.out.println(s3); 34 35 36 37 } 38 39 }
4.6 测试枚举中的常用方法
1 package enumeration; 2 3 import org.junit.Test; 4 5 //枚举的作用:限定某些程序运行时所需要数据的取值范围 6 //带抽象方法的枚举 实例化时必须实现抽象方法 7 //枚举中声明的每一个枚举值表示枚举类的一个实例化对象 8 //m枚举类的构造函数必须是私有的,因为枚举就是用来限制实例化的对象的,如果构造函数为公有的就可以实例化新的对象,不符合枚举规范 9 10 enum Grade { 11 A("100-90") { 12 @Override 13 public String getLocalValue() { 14 // TODO Auto-generated method stub 15 return "优"; 16 } 17 }, 18 B("89-80") { 19 @Override 20 public String getLocalValue() { 21 // TODO Auto-generated method stub 22 return "良"; 23 } 24 }, 25 C("79-70") { 26 @Override 27 public String getLocalValue() { 28 // TODO Auto-generated method stub 29 return "中"; 30 } 31 }, 32 D("69-60") { 33 @Override 34 public String getLocalValue() { 35 // TODO Auto-generated method stub 36 return "差"; 37 } 38 };// 对枚举进行实例化 必须实现抽象方法 39 40 private String value;// 定义每个枚举类型里面存的值 41 42 private Grade(String value) { 43 this.value = value; 44 } 45 46 public String getValue() { // 外部对象获取每个枚举类型里面存的值 47 return this.value; 48 } 49 50 // 带抽象方法的枚举 51 public abstract String getLocalValue(); 52 } 53 54 public class DefineEnum { 55 @Test 56 public void test() { 57 printGrade(Grade.B); 58 } 59 60 public void printGrade(Grade a) { 61 System.out.println(a.getValue()); 62 System.out.println(a.getLocalValue()); 63 64 } 65 66 // 测试枚举的常用方法 67 @Test 68 public void test2() { 69 System.out.println(Grade.A.name());// name()方法返回枚举的名字 70 System.out.println(Grade.A.ordinal());// ordinal()方法返回枚举对象在枚举类中的序号 71 String str = "B"; 72 Grade g = Grade.valueOf(Grade.class, str);// valueOf()方法将某个枚举对象对应的字符串转变为枚举对象 73 // 实际开发中用于检测用户提交的字符串是否属于自己定义的枚举类型 74 // Grade 75 // g=Grade.valueOf(str); 76 System.out.println(g); 77 // for(Grade g:Grade.values()){ //values()方法获取枚举中的每个值 78 // System.out.println(g); 79 // } 80 81 } 82 }
4.7 定义一个星期的枚举设置方法返回对应中文星期
1 package enumeration; 2 3 import org.junit.Test; 4 5 //定义一个星期的枚举WeekDay 6 //枚举值:Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday 7 //该枚举有一个方法,调用该方法返回中文格式的日期 8 enum WeekDay{ 9 Monday { 10 @Override 11 public String getChineseDay() { 12 // TODO Auto-generated method stub 13 return "星期一"; 14 } 15 }, 16 Tuesday { 17 @Override 18 public String getChineseDay() { 19 // TODO Auto-generated method stub 20 return "星期二"; 21 } 22 }, 23 Wednesday { 24 @Override 25 public String getChineseDay() { 26 // TODO Auto-generated method stub 27 return "星期三"; 28 } 29 }, 30 Thursday { 31 @Override 32 public String getChineseDay() { 33 // TODO Auto-generated method stub 34 return "星期四"; 35 } 36 }, 37 Friday { 38 @Override 39 public String getChineseDay() { 40 // TODO Auto-generated method stub 41 return "星期五"; 42 } 43 }, 44 Saturday { 45 @Override 46 public String getChineseDay() { 47 // TODO Auto-generated method stub 48 return "星期六"; 49 } 50 }, 51 Sunday { 52 @Override 53 public String getChineseDay() { 54 // TODO Auto-generated method stub 55 return "星期天"; 56 } 57 }; 58 public abstract String getChineseDay(); 59 60 } 61 public class HomeWork { 62 63 @Test 64 public void test(){ 65 printDay(WeekDay.Friday); 66 } 67 public void printDay(WeekDay wd){ 68 System.out.println(wd.getChineseDay()); 69 } 70 71 }
5. 反射
5.1 反射的概念
反射:加载类(获取类的字节码),获取类里面的各种组成成分
反射的用途:写框架
通过反射获取类里面的各种组成干什么?
(1) 获取构造函数去实例化对象
(2) 获取字段去封装数据
(3) 获取方法去运行
加载类的三种方式:
1 package refelect; 2 3 public class LoadClass { 4 5 public static void main(String[] args) throws ClassNotFoundException { 6 // TODO Auto-generated method stub 7 //加载类的方法 8 //1、Class类的forName()方法 9 Class c1=Class.forName("refelect.Person"); //把类的字节码加载到内存中去 10 System.out.println(c1); 11 //2、通过对象的getClass()方法 12 Person p=new Person(); 13 Class c2=p.getClass(); //把类的字节码加载到内存中去 14 System.out.println(c2); 15 //3、通过.class 16 Class c3=Person.class; //把类的字节码加载到内存中去 17 System.out.println(c3); 18 19 } 20 21 }
5.2 通过反射获取类的构造函数
1 package refelect; 2 3 import java.io.InputStream; 4 import java.util.List; 5 6 public class Person { 7 8 public String name = "aaa"; 9 10 private int password = 123; 11 12 private static int age = 23; 13 14 public static int score = 90; 15 16 public Person() { 17 System.out.println("person"); 18 } 19 20 public Person(String name) { 21 System.out.println("person name"); 22 } 23 24 public Person(String name, int password) { 25 System.out.println("person name password"); 26 } 27 28 private Person(List list) { 29 System.out.println("list"); 30 } 31 32 public void aa() { 33 System.out.println("aa"); 34 } 35 36 public void aa(String name, int password) { 37 System.out.println(name + password); 38 } 39 40 public Class[] aa(String name, int[] password) { 41 return new Class[] { String.class }; 42 } 43 44 private void aa(InputStream in) { 45 System.out.println(in); 46 } 47 48 public static void aa(int num) { 49 System.out.println(num); 50 } 51 52 public static void main(String[] args) { 53 System.out.println("main"); 54 } 55 }
1 package refelect; 2 3 import java.lang.reflect.Constructor; 4 import java.util.ArrayList; 5 import java.util.List; 6 7 import org.junit.Test; 8 9 public class GetConstructor { 10 11 //通过反射获取类里面的无参构造函数 public Person() 12 @Test 13 public void test1() throws Exception{ 14 //1、获取无参构造函数 public Person() 15 Class c1=Class.forName("refelect.Person");//加载类到内存中去 16 Constructor cs1=c1.getConstructor(null);//通过传入的可变参数获取对应的构造函数,此处获取无参构造函数 17 Person p1=(Person) cs1.newInstance(null);//通过无参构造函数实例化对象 18 String s=p1.name; 19 System.out.println(s); 20 } 21 //通过反射获取类里面的有一个参数构造函数 public Person(String name) 22 @Test 23 public void test2() throws Exception{ 24 25 Class c1=Class.forName("refelect.Person");//加载类到内存中去 26 Constructor cs1=c1.getConstructor(String.class);//记住:必须传入String.class告知参数类型为字符串,不能传入"aaa"之流 27 Person p=(Person) cs1.newInstance("aaa");//通过构造函数实例化对象 28 System.out.println(p.name); 29 } 30 //通过反射获取类里面的有两个参数构造函数 public Person(String name,int passord) 31 @Test 32 public void test3() throws Exception{ 33 34 Class c1=Class.forName("refelect.Person");//加载类到内存中去 35 Constructor cs1=c1.getConstructor(String.class,int.class); 36 Person p=(Person) cs1.newInstance("aaa",123); 37 System.out.println(p.name); 38 } 39 //通过反射获取类里面的私有带参构造函数 private Person(List l) 40 @Test 41 public void test4() throws Exception{ 42 //getConstructor()方法只能获取public类型的构造函数 43 //getDeclaredConstructor()方法能够获取所有声明的构造函数包括私有的 44 Class c1=Class.forName("refelect.Person");//加载类到内存中去 45 Constructor cs1=c1.getDeclaredConstructor(List.class); 46 cs1.setAccessible(true);//记住:一定要暴力地将私有的构造函数属性设置为可以被外部访问,否则将会报错 47 Person p=(Person) cs1.newInstance(new ArrayList()); 48 System.out.println(p.name); 49 } 50 //创建对象的另一种途径 直接通过反射出的类的newInstance()方法 ,自动调用无参构造函数实例化,以下代码等价于test1 51 @Test 52 public void test5() throws Exception{ 53 //getConstructor()方法只能获取public类型的构造函数 54 //getDeclaredConstructor()方法能够获取所有声明的构造函数包括私有的 55 Class c1=Class.forName("refelect.Person");//加载类到内存中去 56 Person p=(Person) c1.newInstance(); 57 System.out.println(p.name); 58 } 59 60 }
5.3 通过反射获取类的方法以及main方法的特殊获取
1 package refelect; 2 3 import java.io.FileInputStream; 4 import java.io.InputStream; 5 import java.lang.reflect.Method; 6 7 import org.junit.Test; 8 9 //通过反射获取类里面的方法 10 public class GetMethods { 11 12 //获取方法 public void aa() 13 @Test 14 public void test1() throws Exception{ 15 Class c1=Class.forName("refelect.Person");//加载类 16 Method m=c1.getMethod("aa", null);//获取类里面的方法 第一个参数指定是哪一个方法,第二个参数指定方法的参数类型 17 m.invoke(c1.newInstance(), null);//执行方法,第一个参数指定执行方法需要实例化的对象,第二个参数为需要传入的参数 18 } 19 20 //获取方法 public void aa(String name,int password) 21 @Test 22 public void test2() throws Exception{ 23 Class c1=Class.forName("refelect.Person");//加载类 24 Method m=c1.getMethod("aa", String.class,int.class); 25 m.invoke(c1.newInstance(), "lgs",21); 26 } 27 28 //获取方法 public Class[] aa(String name,int[] password) 29 @Test 30 public void test3() throws Exception{ 31 Class c1=Class.forName("refelect.Person");//加载类 32 Method m=c1.getMethod("aa", String.class,int[].class); 33 Class [] cls=(Class[]) m.invoke(c1.newInstance(), "lgs",new int[]{1,2,3}); 34 System.out.println(cls[0]); 35 } 36 //获取方法 private void aa(InputStream in) 37 @Test 38 public void test4() throws Exception{ 39 Class c1=Class.forName("refelect.Person");//加载类 40 Method m=c1.getDeclaredMethod("aa", InputStream.class);//获取私有方法 41 m.setAccessible(true);//暴力解除方法私有属性 42 m.invoke(c1.newInstance(), new FileInputStream("c:\\1.txt")); 43 44 } 45 //获取方法 public static void aa(int num) 46 @Test 47 public void test5() throws Exception{ 48 Class c1=Class.forName("refelect.Person");//加载类 49 Method m=c1.getDeclaredMethod("aa", int.class);//获取私有方法 50 51 m.invoke(c1.newInstance(), 23);//静态方法调用时不需要对象,因此第一个参数可以为空 52 53 } 54 //获取方法 public static void main(String[] args) 55 @Test 56 public void test6() throws Exception{ 57 Class c1=Class.forName("refelect.Person");//加载类 58 Method m=c1.getDeclaredMethod("main", String[].class);//获取私有方法 59 60 //m.invoke(null, new String[]{"1","2"});//错误的 因为在传数组参数进去给main函数的时候会被自动拆分为String s1和String s2 main函数里面没有这两种参数所以报错参数个数不正确 61 //m.invoke(null, new Object[]{new String[]{"1","2"}});//静态方法调用时不需要对象,因此第一个参数可以为空,此时main函数会被自动拆分为String[]数组,不会报错 62 m.invoke(null, (Object)new String[]{"1","2"});//这哥们不是喜欢把数组拆分为字符串吗,就加Object强制转换骗他我不是数组 63 } 64 }
5.4 通过反射获取类的字段
1 package refelect; 2 3 import java.lang.reflect.Field; 4 5 import org.junit.Test; 6 7 //通过反射获取字段 8 public class GetFields { 9 //获取字段public String name="lgs"; 10 @Test 11 public void test1() throws Exception{ 12 13 Class c1=Class.forName("refelect.Person");//加载类 14 Field f=c1.getField("name");//获取字段 参数为要获取的字段的名称 15 // String s= (String) f.get(c1.newInstance());//获取字段的值 参数为对应的对象 16 Object o=f.get(c1.newInstance());//获取字段的值 参数为对应的对象 17 18 //获取字段的类型 19 Class type=f.getType(); 20 if(type.equals(String.class)){ 21 String s=(String) o; 22 System.out.println(s); 23 } 24 25 //设置字段的值 26 Person p=(Person) c1.newInstance(); 27 f.set(p, "yc"); 28 System.out.println(p.name); 29 30 31 } 32 //获取字段private int password=123; 33 @Test 34 public void test2() throws Exception{ 35 36 Class c1=Class.forName("refelect.Person");//加载类 37 Field f= c1.getDeclaredField("password"); 38 f.setAccessible(true);//暴力修改字段的可视性为公有 39 Object o=f.get(c1.newInstance()); 40 System.out.println(o); 41 } 42 //获取字段private static int age=23; 43 @Test 44 public void test3() throws Exception{ 45 46 Class c1=Class.forName("refelect.Person");//加载类 47 Field f= c1.getDeclaredField("age"); 48 f.setAccessible(true);//暴力修改字段的可视性为公有 49 Object o=f.get(c1.newInstance()); 50 System.out.println(o); 51 } 52 //获取字段public static int score=90; 53 @Test 54 public void test4() throws Exception{ 55 56 Class c1=Class.forName("refelect.Person");//加载类 57 Field f= c1.getField("score"); 58 // Object o=f.get(c1.newInstance()); 59 System.out.println(f.get(c1.newInstance())); 60 } 61 }
6. 内省(Introspector)
6.1 为什么要学习内省
开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性
6.2 通过内省操作javabean属性
1 package refelect; 2 3 /** 4 * Person就是一个javabean 5 * 里面的字段有对应的get或者set方法,这个字段就称为一个属性 6 * 7 * @author lgs 8 * 9 */ 10 public class Person { 11 12 // 字段 13 public String name; 14 15 // 字段 16 private int password; 17 18 // 字段 19 private int age; 20 21 // 字段 22 private int score; 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 public int getPassword() { 33 return password; 34 } 35 36 public void setPassword(int password) { 37 this.password = password; 38 } 39 40 public int getAge() { 41 return age; 42 } 43 44 public void setAge(int age) { 45 this.age = age; 46 } 47 48 public int getScore() { 49 return score; 50 } 51 52 public void setScore(int score) { 53 this.score = score; 54 } 55 56 }
1 package introspector; 2 3 import java.beans.BeanInfo; 4 import java.beans.IntrospectionException; 5 import java.beans.Introspector; 6 import java.beans.PropertyDescriptor; 7 import java.lang.reflect.Method; 8 9 import org.junit.Test; 10 11 //通过内省来操作bean(即Person类)的属性 12 public class TestIntroSpector { 13 14 //获取bean的所有属性 15 @Test 16 public void test1() throws Exception{ 17 //BeanInfo bi=Introspector.getBeanInfo(Person.class);//通过内省Introspector的getBeanInfo(Person.class)方法获取bean的所有属性 18 BeanInfo bi=Introspector.getBeanInfo(Person.class, Object.class);//只获取本身增加的属性,不要从Object类获取的属性 与使用getDisplayName()方法的效果一样 19 PropertyDescriptor [] pd=bi.getPropertyDescriptors();//读取bean的所有属性存到数组pd里面去 20 for(PropertyDescriptor k:pd){ 21 System.out.println(k.getName()); 22 23 } 24 25 } 26 27 //获取并操作bean指定的属性 age 28 @Test 29 public void test2() throws Exception{ 30 Person p=new Person(); 31 //使用构造函数 PropertyDescriptor(String 属性名, 对应的bean)操作特定属性 32 PropertyDescriptor pd=new PropertyDescriptor("age", Person.class); 33 34 //获取属性的写方法,并为属性赋值 35 Method m=pd.getWriteMethod();//获取bean的setAge属性 public void setAge(int age) 36 m.invoke(p, 23);//执行SetAge属性并写入属性值 37 38 //传统方式获取属性的值 39 System.out.println(p.getAge()); 40 41 //内省获取属性的值 42 Method mt=pd.getReadMethod();//获取getAge()方法 读属性值 43 System.out.println(mt.invoke(p, null));//输出属性值 44 45 } 46 //获取bean指定的属性的类型 age 47 @Test 48 public void test3() throws Exception{ 49 Person p=new Person(); 50 //使用构造函数 PropertyDescriptor(String 属性名, 对应的bean)操作特定属性 51 PropertyDescriptor pd=new PropertyDescriptor("age", Person.class); 52 System.out.println(pd.getPropertyType());//获取age属性的类型 53 54 55 } 56 57 }
6.3 使用BeanUtils类操作javabean属性
使用第三方框架beanutils
beanutils的配置过程:
(1)、新建一个开发库文件夹lib,然后向里面添加commons-beanutils-1.8.0.jar和commons-logging.jar包
(2)、同时选中上面的两个jar包-右键-build path-add build path
(3)、当上面的两个jar包在Referenced Libraries(会自动生成这个文件夹)里面变为奶瓶时就可以使用beanutils操作javabean的属性了
1 package refelect; 2 3 import java.util.Date; 4 5 /** 6 * Person就是一个javabean 7 * 里面的字段有对应的get或者set方法,这个字段就称为一个属性 8 * 9 * @author lgs 10 * 11 */ 12 public class Person { 13 14 // 字段 15 public String name; 16 17 // 字段 18 private int password; 19 20 // 字段 21 private int age; 22 23 // 字段 24 private int score; 25 26 //生日 27 private Date birthday; 28 29 public String getName() { 30 return name; 31 } 32 33 public void setName(String name) { 34 this.name = name; 35 } 36 37 public int getPassword() { 38 return password; 39 } 40 41 public void setPassword(int password) { 42 this.password = password; 43 } 44 45 public int getAge() { 46 return age; 47 } 48 49 public void setAge(int age) { 50 this.age = age; 51 } 52 53 public int getScore() { 54 return score; 55 } 56 57 public void setScore(int score) { 58 this.score = score; 59 } 60 61 public Date getBirthday() { 62 return birthday; 63 } 64 65 public void setBirthday(Date birthday) { 66 this.birthday = birthday; 67 } 68 69 }
1 package beanutils; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.text.ParseException; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 import java.util.HashMap; 8 import java.util.Map; 9 10 import org.apache.commons.beanutils.BeanUtils; 11 import org.apache.commons.beanutils.ConversionException; 12 import org.apache.commons.beanutils.ConvertUtils; 13 import org.apache.commons.beanutils.Converter; 14 import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; 15 import org.junit.Test; 16 17 //使用beanutils操作javabean(即Person类)的属性 18 //使用第三方框架beanutils 19 //beanutils的配置过程: 20 //1、新建一个开发库文件夹lib,然后向里面添加commons-beanutils-1.8.0.jar和commons-logging.jar包 21 //2、同时选中上面的两个jar包-右键-build path-add build path 22 //3、当上面的两个jar包在Referenced Libraries(会自动生成这个文件夹)里面变为奶瓶时就可以使用beanutils操作javabean的属性了 23 public class TestBeanUtils { 24 25 //BeanUtils类操作bean属性 26 @Test 27 public void test1() throws Exception, Exception{ 28 Person p=new Person(); 29 //通过BeanUtils的setProperty(bean, name, value)方法设置特定的属性的值 30 //第一个参数代表要设置的bean对象(即Person对象),第二个参数表示要设置的属性名,第三个表示要设置的属性值 31 BeanUtils.setProperty(p, "name", "fp");//设置bean属性的值 32 System.out.println(p.getName()); 33 34 } 35 //BeanUtils类操作用户通过表单提交过来的值 36 @Test 37 public void test2() throws Exception, Exception{ 38 39 //用户通过表单提交过来的值,提交过来的都是字符串 40 String name="lgs"; 41 String password="123"; 42 String age="23"; 43 44 //把用户提交过来的值设置到bean的属性(即Person类的属性)里面去,提交过来的都是字符串 45 // BeanUtils内部能够把表单提交过来的字符串转变为对应的八种基本数据类型,不能转变为日期这种复杂的数据类型 46 //如果要转换为日期就必须为BeanUtils注册日期转换器 47 Person p=new Person(); 48 BeanUtils.setProperty(p, "name", name);//设置bean属性的值 49 BeanUtils.setProperty(p, "password", password);//设置bean属性的值 50 BeanUtils.setProperty(p, "age", age);//设置bean属性的值 BeanUtils能够把表单提交过来的字符串转变为对应的八种基本数据类型 51 System.out.println(p.getName()); 52 System.out.println(p.getPassword()); 53 System.out.println(p.getAge()); 54 55 56 } 57 //把用户提交过来的字符串类型的日期 转换为bean里面定义的日期类型(Date类型)的日期 ,自定义转换器 58 @Test 59 public void test3() throws Exception, Exception{ 60 61 //用户通过表单提交过来的值,提交过来的都是字符串 62 String name="lgs"; 63 String password="123"; 64 String age="23"; 65 //字符串类型的日期 66 String birthday="1995-06-06"; 67 68 //把用户提交过来的值设置到bean的属性(即Person类的属性)里面去,提交过来的都是字符串 69 // BeanUtils内部能够把表单提交过来的字符串转变为对应的八种基本数据类型,不能转变为日期这种复杂的数据类型 70 //如果要转换为日期就必须为BeanUtils注册日期转换器 71 72 //为了让字符串类型的日期赋给bean的birthday(Date类型属性)属性,我们给BeanUtils注册一个日期转换器 73 //ConvertUtils.register(converter, clazz); 74 //第一个参数表示要注册的转换器接口,第二个表示要注册的转换器类型 75 ConvertUtils.register(new Converter(){ 76 public Object convert(Class type, Object value){ 77 if(value==null){ 78 return null; 79 } 80 if(!(value instanceof String)){ 81 throw new ConversionException("只支持String类型的转换"); 82 } 83 String s=(String) value; 84 if(s.trim().equals("")){ 85 return null; 86 } 87 SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");//指定要转换的日期格式 88 try { 89 return sdf.parse(s); 90 } catch (ParseException e) { 91 // TODO Auto-generated catch block 92 //e.printStackTrace(); 93 throw new RuntimeException(e);//异常链e不能断(不能为空),否则不知道抛出的是什么异常 94 } 95 96 97 } 98 }, Date.class); 99 Person p=new Person(); 100 BeanUtils.setProperty(p, "name", name);//设置bean属性的值 101 BeanUtils.setProperty(p, "password", password);//设置bean属性的值 102 BeanUtils.setProperty(p, "age", age);//设置bean属性的值 BeanUtils能够把表单提交过来的字符串转变为对应的八种基本数据类型 103 BeanUtils.setProperty(p, "birthday", birthday);//只支持八种数据类型的转换,所以在此之前已经为BeanUtils注册了日期转换器 104 System.out.println(p.getName()); 105 System.out.println(p.getPassword()); 106 System.out.println(p.getAge()); 107 System.out.println(p.getBirthday().toLocaleString()); 108 109 110 } 111 //把用户提交过来的字符串类型的日期 转换为bean里面定义的日期类型(Date类型)的日期 112 //使用BeanUtils已经写好的转换器 113 @Test 114 public void test4() throws Exception, Exception{ 115 116 //用户通过表单提交过来的值,提交过来的都是字符串 117 String name="lgs"; 118 String password="123"; 119 String age="23"; 120 //字符串类型的日期 121 String birthday="1995-06-06"; 122 123 //注册日期转换器时使用BeanUtils写好的日期转换器 124 //既然已经写好了日期转换器为什么还要自己写呢原因有2个 125 //1、写好的日期转换器DateLocaleConverter有bug,当转入日期为空时,仍会转空日期 此时会报错 126 //2、还有一些转换器没有写,这样就需要自己来写转换器了 127 ConvertUtils.register(new DateLocaleConverter(birthday), Date.class); 128 129 Person p=new Person(); 130 BeanUtils.setProperty(p, "name", name);//设置bean属性的值 131 BeanUtils.setProperty(p, "password", password);//设置bean属性的值 132 BeanUtils.setProperty(p, "age", age);//设置bean属性的值 BeanUtils能够把表单提交过来的字符串转变为对应的八种基本数据类型 133 BeanUtils.setProperty(p, "birthday", birthday);//只支持八种数据类型的转换,所以在此之前已经为BeanUtils注册了日期转换器 134 System.out.println(p.getName()); 135 System.out.println(p.getPassword()); 136 System.out.println(p.getAge()); 137 System.out.println(p.getBirthday()); 138 139 140 } 141 142 //定义Map集合存放用户提交的数据 143 @Test 144 public void test5() throws Exception, InvocationTargetException{ 145 //定义Map集合封装用户提交的数据 //Map的关键字必须与bean的属性一致,否则不能写入 146 Map m=new HashMap(); 147 m.put("name", "yqh"); 148 m.put("password", "123456"); 149 m.put("age", "20"); 150 m.put("birthday", "1995-12-30"); 151 152 //为BeanUtils注册日期转换器转换字符串日期 153 ConvertUtils.register(new DateLocaleConverter(), Date.class); 154 Person bean=new Person(); 155 BeanUtils.populate(bean, m);//用Map集合填充bean属性 156 System.out.println(bean.getName()); 157 System.out.println(bean.getPassword()); 158 System.out.println(bean.getAge()); 159 System.out.println(bean.getBirthday()); 160 } 161 162 }
7. 泛型
7.1 使用泛型(generic)的好处
使用泛型的好处可以进行类型安全检查,提高了程序的可读性和稳定性
1 package generic; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import org.junit.Test; 7 8 public class TestGeneric { 9 10 //使用泛型的好处 11 @Test 12 public void test1() { 13 // TODO Auto-generated method stub 14 // 1、最原始的集合 15 List l = new ArrayList(); 16 l.add("aaa"); 17 // 此时集合中的值明显是一个字符串,但程序员却把字符串转为Integer 编译不会出错但运行会错,相当不安全 18 // Integer i=(Integer) l.get(0); 19 20 // 使用泛型的好处 21 // 2、定义集合时就利用泛型指定其处理的类型 这样做提高了程序的可读性和稳定性,同时 进行类型安全检查 22 List<String> l1 = new ArrayList<String>(); 23 // l1.add(1);//使用泛型的好处可以进行类型安全检查 因为指定的泛型是String,所以传入的值必须为字符串 24 l1.add("1"); 25 System.out.println(l1.get(0)); 26 27 } 28 29 }
7.2 泛型与集合搭配使用及其注意事项
泛型是给java编译器(javac)使用的;防止在编译阶段向集合插入非法数据,当编译完以后,编译后的class文件不具有泛型信息,所以不影响运行效力,这个过程称之为“擦除”
1 package generic; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.Iterator; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Map.Entry; 9 import java.util.Set; 10 11 import org.junit.Test; 12 13 public class TestGeneric { 14 15 //使用泛型的好处 16 @Test 17 public void test1() { 18 // TODO Auto-generated method stub 19 // 1、最原始的集合 20 List l = new ArrayList(); 21 l.add("aaa"); 22 // 此时集合中的值明显是一个字符串,但程序员却把字符串转为Integer 编译不会出错但运行会错,相当不安全 23 // Integer i=(Integer) l.get(0); 24 25 // 使用泛型的好处 26 // 2、定义集合时就利用泛型指定其处理的类型 这样做提高了程序的可读性和稳定性,同时 进行类型安全检查 27 List<String> l1 = new ArrayList<String>(); 28 // l1.add(1);//使用泛型的好处可以进行类型安全检查 因为指定的泛型是String,所以传入的值必须为字符串 29 l1.add("1"); 30 System.out.println(l1.get(0)); 31 32 } 33 //集合的存取 34 @Test 35 public void test2() { 36 // TODO Auto-generated method stub 37 38 List<String> l1 = new ArrayList<String>(); 39 // l1.add(1);//使用泛型的好处可以进行类型安全检查 因为指定的泛型是String,所以传入的值必须为字符串 40 //集合的存 41 l1.add("1"); 42 l1.add("2"); 43 l1.add("3"); 44 //集合的取 45 //1、传统方案:使用迭代器 46 System.out.println("-------1、传统方案:使用迭代器-----"); 47 Iterator<String> it=l1.iterator(); 48 while(it.hasNext()){ 49 System.out.println(it.next()); 50 } 51 //2、增强for循环 52 System.out.println("-------2、增强for循环-----"); 53 for(String s:l1){ 54 System.out.println(s); 55 } 56 57 } 58 59 //Map集合的存取 60 @Test 61 public void test3(){ 62 Map<Integer,String> m=new HashMap<Integer,String>(); 63 //Map的存 64 m.put(1, "aa"); 65 m.put(2, "bb"); 66 m.put(3, "cc"); 67 //Map的取 68 //传统方式1:keySet; 69 System.out.println("-------传统方式1:keySet-------"); 70 Set<Integer> s=m.keySet(); 71 Iterator<Integer> it=s.iterator(); 72 while(it.hasNext()){ 73 int i=it.next(); 74 System.out.println(i+" "+m.get(i)); 75 } 76 77 //传统方式2:entrySet 常用 78 System.out.println("-------传统方式2:entrySet-------"); 79 Set<Entry<Integer, String>> se=m.entrySet(); 80 Iterator<Entry<Integer, String>> ite=se.iterator(); 81 while(ite.hasNext()){ 82 Entry<Integer, String> e=ite.next(); 83 System.out.println(e.getKey()+" "+e.getValue()); 84 } 85 86 //增强for循环 j2ee里面常用的方式(重点) 87 System.out.println("-------增强for循环-------"); 88 for(Entry<Integer, String> e:m.entrySet()){ 89 System.out.println(e.getKey()+" "+e.getValue()); 90 } 91 } 92 //使用泛型时应该注意的问题 93 @Test 94 public void test4(){ 95 //注意:当集合两边都使用泛型时,两边的泛型类型必须一致,否则会报错 96 //但是如果只有一边使用泛型是是可以的 97 98 //两边都使用泛型 99 ArrayList<String> al=new ArrayList<String>(); 100 //ArrayList<String> al=new ArrayList<Object>(); 101 //ArrayList<Object> al2=new ArrayList<String>(); 102 103 //只有一边使用泛型 104 ArrayList<String> al3=new ArrayList(); 105 ArrayList al4=new ArrayList<String>(); 106 107 //提高阶段:泛型是给java编译器(javac)使用的;防止在编译阶段向集合插入非法数据, 108 //当编译完以后,编译后的class文件不具有泛型信息,所以不影响运行效力,这个过程称之为“擦除” 109 //如l1和l2编译成class文件以后,除了变量名称不一样,其他的是完全一样的 110 List<String> l1 = new ArrayList<String>(); 111 List l2 = new ArrayList(); 112 } 113 114 }
7.3 自定义泛型
定义泛型方法:泛型遵循先声明再使用的规则
第一种方式:常规方法
1 package generic; 2 3 //定义泛型方法 4 public class DefineGenericMetgod { 5 6 public void testaa(){ 7 aa("aa");//传入的是字符串,就确定了泛型类型为String,就不存在类型强转的情况(这就是泛型的真正意义) 8 } 9 //泛型遵循先声明再使用的规则 10 public <T> void aa(T t){ 11 12 } 13 public <E, K, T> void bb(T t,K k,E e){ 14 15 } 16 17 }
第二种方式:一个泛型作用于多个方法时可以把泛型声明在类上
1 package generic; 2 3 //定义泛型方法 4 //当一个泛型作用于多个方法时可以把泛型声明在类上 5 public class DefineGenericMetgod2<T,K,E> { 6 7 public void testaa(){ 8 //aa("aa");//传入的是字符串,就确定了泛型类型为String,就不存在类型强转的情况(这就是泛型的真正意义) 9 } 10 //泛型遵循先声明再使用的规则 11 public T aa(T t){ 12 return t; 13 14 } 15 public void bb(T t,K k,E e){ 16 17 } 18 //类上的泛型对静态方法不起作用,要重新自定义自己的泛型,注意这个泛型T和类上的泛型T不是同一个 19 public static<T> void cc(T t){ 20 21 } 22 23 }
7.4 练习
(1) 编写一个泛型方法,交换指定数组元素的位置,记住:一定要使用泛型T定义方法,不能使用诸如String,int子类的类型,那样的话就只能交换一种特定类型的数组
1 public <T> void swap(T arr[], int pos1, int pos2) { 2 T temp; 3 temp = arr[pos1]; 4 arr[pos1] = arr[pos2]; 5 arr[pos2] = temp; 6 } 7 8 @Test 9 public void testSwap() { 10 Integer[] i = new Integer[] { 1, 2, 3, 4, 5 }; 11 System.out.println("-----交换之前-----"); 12 for (Integer k : i) { 13 System.out.println(k); 14 } 15 swap(i, 1, 3); 16 System.out.println("-----交换之后-----"); 17 for (Integer k : i) { 18 System.out.println(k); 19 } 20 21 }
(2) 编写一个泛型方法,接收任意数组,然后颠倒数组里面的元素
思路:定义两个指针,第一个指针指向数组的第一个元素,第二个指针指向数组的最后一个元素,把两个元素交换,然后把第一个指针加1,第二个指针减1,再交换,如此进行下去知道两个指针的值相同时不在进行交换
1 public <T> void reverse(T arr[]) { 2 int first = 0; 3 int last = arr.length - 1; 4 5 while (true) { 6 if (first >= last) { 7 break; 8 } 9 T temp; 10 temp = arr[first]; 11 arr[first] = arr[last]; 12 arr[last]=temp; 13 first++; 14 last--; 15 } 16 } 17 18 @Test 19 public void testReverse() { 20 Integer[] i = new Integer[] { 1, 2, 3, 4, 5 }; 21 System.out.println("-----颠倒之前-----"); 22 for (Integer k : i) { 23 System.out.println(k); 24 } 25 reverse(i); 26 System.out.println("-----颠倒之后-----"); 27 for (Integer k : i) { 28 System.out.println(k); 29 } 30 }
8、读取控制台单行或者多行输入
package com.study.topic; import java.util.ArrayList; import java.util.List; import java.util.Scanner; // 读取键盘输入:可以读多行或者一行 public class ReadKeyBoardInput { public static void main(String[] args) { // 1. 接收键盘的多行输入,用while(true)循环,控制什么时候跳出(注意控制跳出的条件判断),next()和nextLine()都可以使用 // moreLineInputCommonByNextNextLine(); // 2.接收键盘多行输入,使用next() // moreLineInputByNext(); // 3.接收键盘单行输入,使用nextLine() singleLineInputByNextLine(); } public static void singleLineInputByNextLine() { Scanner sc = new Scanner(System.in); String nextLine = sc.nextLine(); System.out.println(nextLine); sc.close(); } // 接收键盘多行输入,使用next() // 注意有几个Scanner sc.next() 就需要输入多少行,否则会阻塞在没有输入第n行的第n个sc.next()一直等待输入 // 注意while(sc.hasNext())循环结束的标志是永远不会退出的,因为sc.hasNext()会一直阻塞等待下一次输入 // 所以一旦确认需要的多行输入已经获得就主动break; public static void moreLineInputByNext() { Scanner sc = new Scanner(System.in); while(sc.hasNext()) { String row1 = sc.next(); String row2 = sc.next(); String row3 = sc.next(); String row4 = sc.next(); System.out.println("row1:" + row1); System.out.println("row2:" + row2); System.out.println("row3:" + row3); System.out.println("row4:" + row4); break; } sc.close(); } // 接收键盘的多行输入,用while循环控制什么时候跳出(注意控制跳出的条件判断),next()和nextLine()都可以使用 public static void moreLineInputCommonByNextNextLine() { // 1. 读多行 Scanner scMoreLine = new Scanner(System.in); // 把每次读到的数据放入readDataLst集合 List<String> readDataLst = new ArrayList<>(); while(true) { // next()可以读多行数据 nextLine()只可以读一行数据 这里用while循环处理的所以两种都可以用 String next = scMoreLine.nextLine(); if(null != next && !"".equals(next)) { readDataLst.add(next); } // 注意控制跳出的条件判断 if (readDataLst.size() == 3) { break; } } scMoreLine.close(); System.out.println(readDataLst); } }