Java基础:【第四章】IO流 + 反射机制 + 注解 |
您所在的位置:网站首页 › java反射底层 › Java基础:【第四章】IO流 + 反射机制 + 注解 |
一、IO流
1. IO流的分类
重点掌握: 文件流:FileInputStream、FileOutputStream【文件拷贝】 输出流:PrintStream 【日志】 对象流:ObjectInputStream、ObjectOutputStream 读取 和 写入 InputStream、Reader:从硬盘到内存,读取。OutputStream、Writer:从内存到硬盘,写出。将自己当作内存,从外读取,从内写出。节点流 和 处理流(包装流) 节点流 直接与数据源相连,用于输入或者输出处理流:在节点流的基础上对之进行加工,进行一些功能的扩展处理流的构造器必须要 传入节点流的子类 2. 文件流FileInputStream: int available() : 返回总剩余字节数 long skip(long n) : 跳过并丢弃n个字节数据 int read() : 返回读取到字节的int类型值,(范围0-255)若读取不到数据,返回-1。 int read(byte[] b) : 返回读取到字节的数量,若读取不到数据,返回-1。一次最多读取 b.length 字节。 String(byte[] bytes) : 将byte类型的数组bytes转换成字符串输出 String(byte[] bytes, int offset, int length) : 将byte类型的数组bytes按照指定起始位置转换成字符串输出。 public class FileInputStreamTest{ public static void main(String[] args){ FileInputStream fis = null; try{ //打开字节输入流 fis = new FileInputStream("文件的绝对路径"); //文件的总字节数量 System.out.println("文件的总字节数量:" + fis.available()); //创建一个临时byte数组,容量为4,一次读取4个字节 byte bytes = new byte[4]; //读取数据 int readCount = 0; while((readCount = fis.read(bytes)) != -1){ String str = new String(bytes,0,readCount); System.out.print(str); } //捕获异常 }catch(FileNotFoundException e){ e.printStackTrack(); }catch(IOException e){ e.printStackTrack(); }finally{ //关闭流 if(fis != null){ try{ fis.close(); }catch(IOException e){ e.printStackTrace(); } } } } }FileOutputStream: public class FileOutputStream{ public static void main(String[] args){ FileOutputStream fos = null; try{ //开启写入,在文件后追加写入append:true fos = new FileOutputStream("保存文件的绝对路径",true); //写入的内容 byte[] bytes = {97,98,99,100}; //将指定字节传入数据源 fos.write(bytes); //写入完要刷新 fos.flush(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally{ //写完之后要关闭流 if(fos != null){ try{ fos.close(); }catch(IOException e){ e.printStackTrace(); } } } } }文件的拷贝 public class CopyFile{ public static void main(String[] args){ FileInputStream fis = null; FileOutputStream fos = null; try{ //开启文件输入流 fis = new FileInputStream("读取文件的绝对路径"); //开启文件输出流 fos = new FileOutputStream("文件保存的绝对路径"); //一边读取,一边写入 byte[] bytes = new byte[1024 * 1024];//设置每次读取的容量:1MB //读取数据 int readCount = 0; while(( readCount = fis.read(bytes)) != -1){ //写入数据:读到哪,写到哪 fos.write(bytes,0,readCount); } //文件输出流刷新 fos.flush(); }catch(FileNotFoundException e){ e.printStrackTrack(); }catch(IOException e){ e.printStrackTrack(); }finally{ //关闭输入流 if(fis != null){ try{ fis.close(); }catch(IOException e){ e.printStackTrack(); } } //关闭输出流 if( fos != null){ try{ fos.close(); }catch(IOException e){ e.printStackTrack(); } } } } }字符流:Reader 和 Writer 节点流 只要将byte[] 换成 char[],框架一样 转换流:InputStreamReader 和 OutputStreamWriter 处理流InputStreamReader: InputStream --> ReaderOutputStreamWriter: OutputStream --> Writer能指定字符编码缓冲流: 处理流 自带缓冲,不需要写自定义byte[]数组,或者char[]数组,适用于大量数据读写操作 传入Reader或者Writer 可以使用readLine()方法 数据流: DataInputStream 、 DataOutputStream 处理流 把数据和数据类型一起按顺序写入文件当中 输出流:PrintStream 和 PrintWriter PrintWriter 可以打印各种数据类型 可以改变输出方向,setOut( printWriter,printStream ) : 重定向 3. 输出流可以控制输出方向,默认向控制台输出。 System.setOut(out);//向自定义路径文件输出 日志工具类 import java.util.Date; public class Logger{ public static void log(String msg){ PrintStream out = null; try{ //输出流 out = new PrintStream(new FileOutputStream("保存文件的绝对路径",true)); //设置输出方向,默认向控制台输出,改为向日志的绝对路径输出 System.setOut(out); //日期时间 Data nowTime = new Data(); SimpleDataFormat sdf = new SimpleDataFormat("yyyy-MM-dd HH:mm:ss SSS"); String strTime = sdf.format(nowTime); //输出当前时间 System.out.println(strTime + msg); }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally{ //关闭输出流 if( out != null){ try{ out.close(); }catch(IOException e){ e.printStackTrace(); } } } } } 4. 对象流序列化 和 反序列化 序列化: 将对象以二进制的形式保存在硬盘上 ,叫序列化。 反序列化: 将二进制的文件转化为对象读取 ,叫做反序列化。 序列化步骤:ObjectOutputStream 创建序列化对象s,并且对象implements Serializable接口创建序列化oos,传入节点流FileOutputStream,设置文件的保存路径序列化调用oos.writeObject(s)因为是输出流,需要刷新,oos.flush()因为是流,需要关闭,oos.close()注:Serializable接口,作用起到标识的作用,表明该类支持序列化。JVM会默认为该类生成序列化版本号 serialVersionUID 。 序列化版本号的作用:区分类 区分类:一看类名,二看序列化版本号,两者相同,表明是同一个类 【新】流里的序列化版本号与【旧】本地序列化版本号相同,表明是同一个类,才能序列化。 【新】流里的序列化版本号与【旧】本地序列化版本号不同,表明不是同一个类,不能序列化,会出现运行时异常InvalidClassException。 自动生成的序列化版本号的缺点: 一旦代码确定,不能进行修改。如果修改,后续编译,会生成新的类,这样就不是同一个类了。所以如果一个类实现了Serializable接口,提供一个不变的序列化版本号。 private static final long serialVersionUID = -12315456465123L;反序列化步骤:ObjectInputStream 创建反序列化ois,传入节点流FileInputStream,设置文件的读取路径反序列化ois.readObject(),返回一个对象关闭反序列化流ois.close() 5. 文件夹File类的常用方法: new File(“路径名”)构造方法f.exists()判断文件是否存在f.createNewFile()创建文本文件f.mkdir()创建文件夹f.mkdirs()创建多重文件夹f.getParent()获取文件父路径,返回String类型f.getAbsolutePath()获取文件绝对路径……如何使用IO流读取文件 File file = new File("d:/spring.txt"); try { Reader reader = new FileReader(file); BufferedReader buffered = new BufferedReader(reader); String data = null; while((data = buffered.readLine())!=null){ System.out.println(data); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }递归读取文件夹文件 package test; import java.io.File; /** * * 递归读取文件夹的文件 */ public class ListFileDemo { public static void listFile(String path) { if (path == null) { return;// 因为下面的new File如果path为空,回报异常 } File[] files = new File(path).listFiles(); if (files == null) { return; } for(File file : files) { if (file.isFile()) { System.out.println(file.getName()); } else if (file.isDirectory()) { System.out.println("Directory:"+file.getName()); listFile(file.getPath()); } else { System.out.println("Error"); } } } public static void main(String[] args) { ListFileDemo.listFile("D:\\data"); } } 6. IO流的面试总结https://blog.csdn.net/baidu_37107022/article/details/76890019 二、反射机制 1. 反射机制的定义和原理 一种间接操作目标对象的机制原理是JVM运行期在加载.class文件时,除了会在堆内存中生成该类的对象,还会动态创建Class对象,每个类只有一个Class对象,并通过Class对象反向编译其属性和方法 2. 反射机制的优缺点优点: 可以让程序更加灵活,符合OCP原则。 通过反射机制,创建的对象,可以通过修改配置文件,创建不同的实例对象。 缺点: 反射会消耗一定的系统资源。 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题 3. 反射机制的应用 反编译:.class --> .java通过反射机制访问java对象的属性,方法,构造方法等开发各种通用框架,比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean)。为了保证框架的通用性 ,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。 4. 反射机制常用的类java.lang.Class: 代表整个字节码,代表一个类型,代表整个类 java.lang.reflect.Field: 代表字节码中的属性字节码。代表类中的成员变量(静态变量) java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法 java.lang.reflect.Method: 代表字节码中的方法字节码。代表类中的方法 java.lang.reflect.Modifier :返回int类型的修饰关键字标号 5. 反射机制的应用获取Class的三个主要方法 引用对象.getClass() : 缺点:先要创建对象 类名.class : 每个类都有class属性。缺点:需要导入包,否则抛编译错误。 Class.forName() 【推荐】:可以在配置文件修改参数 注:在JVM运行阶段,对一个类,只会动态生成一个Class对象。 以下stu1、stu2、stu3是同一个Class对象 public class ReflectTest{ public static void main(String[] args){ //引用对象.getClass() Student s = new Student(); Class stu1 = s.getClass(); //类名.class Class stu2 = Student.class; //Class.forName() try{ Class stu3 = Class.forName("reflect.Student");//包名.类名 }catch(FileNotFound e){ e.printStackTrace(); } } }反射应用: 通过反射创建对象获取对象所有/单个属性,修改其中的一个属性值获取对象的所有/单个方法,并调用反射main方法具体实现:【IO流 + Properties + 反射机制】 加载配置文件,并获取value值: 通过线程获取配置文件,传入输入流,再加载进Properties集合中,并通过getProperties()获取value值创建对象:获取对象,并实例化对象 通过Class.forName()获取对象,通过有无参数构造方法实例化对象。获取对象的所有声明属性 : getDeclaredFields() getFields() : 获取public修饰的字段,无法获取私有、受保护、默认的属性获取某个属性,并修改属性值 getDeclaredField(): 获取单个属性setAccessible(true):暴力访问set(对象,值) :修改获取对象的单个方法,并调用 通过method.invoke(),传入反射的对象,和参数列表。调用对象的方法反射main方法:invoke(null,(Object)new String[]{“a”,“b”,“c”})null: 因为main是static修饰的方法,null符合(Object)new String[]{“a”,“b”,“c”}: 因为JDK1.5之后String[]从数组变成了可变数组,所以要将String对象拆分成3个对象,所以要强制转换。 public class ReflectObject{ public static void main(String[] args){ try{ //*--------------------------------------------- //1.加载配置文件,并获取value值 //先打开文件输入流,将配置文件传入流 //InputStream path = new InputStream("D:\\src\\test.properties"); //改进,使用线程获取配置文件,不用再new对象。注意点:读取的是src包下的文件 InputStream path = Thread.currentThread().getcontextClassLoader().getResourceAsStream("test.properties"); //创建Properties类,存放配置文件。注意:Properties传入的流是Reader、InputStream Properties pt = new Properties(); //再将配置文件加载到Properties类中 pt.load(path); //通过key读取value值 String className = pt.getProperties("className"); //*--------------------------------------------- //2.创建对象:获取对象,并实例化对象 //获取class对象 Class c = Class.forName(className); //方法一: 通过无参数构造方法创建对象 //1.通过newInstance()方法,创建对象 Objcet o1 = c.newInstance();//调用指定类的无参数构造方法,若该类中没有无参数构造方法抛出异常 //2.通过构造器,创建对象,再修改字段的值 Object o2 = c.getConstructor().newInstance(); //方法二: 通过有参数构造方法创建对象//对象中的属性.class Object o3 = c.getConstructor(String.class).newInstance("xxx"); //*--------------------------------------------- //3.获取对象的所有声明属性 Field[] fields = o1.getDeclaredFields(); for(Field f : fields){ int i = f.getModifer(); System.out.print("修饰符列表:" + Modifer.toString(i) + "返回类型:" + f.getType() + "属性名:" + f.getName() ); } //*--------------------------------------------- //4.获取某个属性,并修改属性值 Field f = c.getDeclaredField("money"); //money是private修饰的属性,暴力访问 f.setAccessible(true); //传入要修改的对象,修改值 f.set(o1,555); //*--------------------------------------------- //5.获取某个方法,指定方法名+参数列表的类型.class(只能放在类名后) Method method = c.getDeclaredMethod("login",String.class,String.class); //通过invoke(),传入反射的对象,和参数列表。调用对象的方法 Object res = method.invoke(o1,"admin","123456"); System.out.println(res); }catch(FileNotFoundException e){ e.printStackTrace(); }finally{ //关闭流 if(path != null){ try{ path.close(); }catch(IOException e){ e.printStackTrace(); } } } } } Class.forName("带包名的完整的类名");//写配置文件className=包名.类名 重点: 如果只想要静态方法执行,可以使用 Class.forName("带包名的完整的类名"); 执行该方法:再类加载时,静态方法块执行。参考链接: https://blog.csdn.net/a745233700/article/details/82893076?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase https://blog.csdn.net/sinat_38259539/article/details/71799078 三、注解介绍 Annotation注解,是java5.0引入的注解机制,与class和interface一样,注解也是一种类型。 注解的定义:比interface前多了一个 @ public @interface Test{ }注解作用 java的类、属性、方法等都可以使用注解,相当于标签 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 编译时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。 运行时的处理: 可以在程序运行的时候通过反射机制提取注解 值得注意的是,注解不是代码本身的一部分, 注解对于代码的运行效果没有直接影响。 元注解 :作用在其他注解上的注解 元注解:@Target、@Retention、@Doumented、@Inherited、@Repeatable @Target() : 目标注解,指定注解运用的场景 ElementType.ANNOTATION_TYPE 可以给一个注解进行注解ElementType.CONSTRUCTOR 可以给构造方法进行注解ElementType.FIELD 可以给属性进行注解ElementType.LOCAL_VARIABLE 可以给局部变量进行注解ElementType.METHOD 可以给方法进行注解ElementType.PACKAGE 可以给一个包进行注解ElementType.PARAMETER 可以给一个方法内的参数进行注解ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举@Retention() : 保留注解,表明注解保留的时间 RetentionPolicy.SOURCE 注解只保留在源码阶段,在编译器进行编译时它将被丢弃忽视。RetentionPolicy.CLASS 注解只被保留到编译阶段,它并不会被加载到 JVM 中。RetentionPolicy.RUNTIME 注解可以保留到运行阶段,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。@Documented : 文档注解,将注解的内容送到javadoc中 @Inherited :继承注解 @Repeatable:可重复注解 @interface Persons { Person[] value(); } @Repeatable(Persons.class) @interface Person{ String role default ""; } @Person(role="artist") @Person(role="coder") @Person(role="PM") public class SuperMan{ }其他常用注解: @Override - 子类要重写父类@Override修饰的方法 @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。 @SuppressWarnings - 告诉编译器禁止特定警告。在与泛型出现之前编写的遗留代码接口时最常用的 @FunctionalInterface - 在Java 8中引入,表明类型声明是一个功能接口,可以使用Lambda Expression提供其实现 注解的属性 注解的属性叫做成员变量。注解中只有属性,没有方法。 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { int id(); String msg(); }赋值的方式是在注解的括号内以 value=” ” 形式,多个属性之前用 ,隔开。 @TestAnnotation(id=3,msg="hello annotation") public class Test {}需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。 注解中属性可以有默认值,默认值需要用 default 关键值指定。比如: @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { public int id() default -1; public String msg() default "Hi"; }TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。 它可以这样应用。 @TestAnnotation()public class Test {}因为有默认值,所以无需要再在 @TestAnnotation 后面的括号里面进行赋值了,这一步可以省略。 另外,还有一种情况。如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。 public @interface Check { String value(); }上面代码中,Check 这个注解只有 value 这个属性。所以可以这样应用。 @Check("hi") int a;这和下面的效果是一样的 @Check(value="hi") int a;最后,还需要注意的一种情况是一个注解没有任何属性。比如 public @interface Perform {}那么在应用这个注解的时候,括号都可以省略。 @Performpublic void testMethod(){}注解通过反射提取 通过Class对象的isAnnotationPresent()判断它是否使用了注解 通过getAnnotation()或者getAnnotations()方法,获取单个注解或者所有注解对象 如果获取到的 Annotation 如果不为 null,则可以通过注解对象调用它们的属性方法了。 @TestAnnotation() public class Test { public static void main(String[] args) { boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class); if ( hasAnnotation ) { TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class); System.out.println("id:"+testAnnotation.id()); System.out.println("msg:"+testAnnotation.msg()); } } } @TestAnnotation(msg="hello") public class Test { @Check(value="hi") int a; @Perform public void testMethod(){} @SuppressWarnings("deprecation") public void test1(){ Hero hero = new Hero(); hero.say(); hero.speak(); } public static void main(String[] args) { boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class); if ( hasAnnotation ) { TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class); //获取类的注解 System.out.println("id:"+testAnnotation.id()); System.out.println("msg:"+testAnnotation.msg()); } try { Field a = Test.class.getDeclaredField("a"); a.setAccessible(true); //获取一个成员变量上的注解 Check check = a.getAnnotation(Check.class); if ( check != null ) { System.out.println("check value:"+check.value()); } Method testMethod = Test.class.getDeclaredMethod("testMethod"); if ( testMethod != null ) { // 获取方法中的注解 Annotation[] ans = testMethod.getAnnotations(); for( int i = 0;i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |