目录
解析:
1.平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
1.public PrintStream(String fileName):作用: 使用指定的文件名创建一个新的打印流。
构造使用演示:
- // 创建一个打印流对象
- PrintStream printStream = new PrintStream("dd.txt");
特别注意 :若这文件不存在则会创建 若源文件存在且里面有数据将会被清除。
System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控住台上,当我们需将某些结果或者信息打印在我们指定的文件中时,这个时候就需要改变打印的流向了,
比如:一些日志信息,一些导致异常的信息等等。
改变流向代码演示:
- package com.feisi.week6.day5;
-
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.PrintStream;
- import java.io.UnsupportedEncodingException;
-
- public class Test4 {
- public static void main(String[] args) {
- // 打印流
- try {
- // 创建一个打印流对象 若这文件不存在则会创建,false表示清除该文件原有数据,true表示追加数据
- PrintStream printStream = new PrintStream(new FileOutputStream("dd.txt",true));
- // 改变流向之前会打印在控住台
- System.out.println(98);
- // 直接使用对象将信息写入文件中
- printStream.println(12345);
-
- // 设置系统的流向打印到指定文件中
- System.setOut(printStream);
-
- System.out.println("张三");
- // 打印流是自定刷新的
- System.out.println(true);
- // 关闭流
- printStream.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
运行结果:
控住台:
文件中:
解析:
1.java.util.Properties类继承与Hashtable,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。
2.该类是Hashtable集合类的一个子类对象,Hashtable集合类现在已经被HashMap代替。
3.该类是一个Map集合。之前学习的集合都是将数据存储到内存中,但是这个集合类它可以和IO流结合,直接把集合中的数据保存在硬盘的文件中,或者直接从硬盘的文件中加载数据保存在集合中;
4.这个集合中的键和值都是String类型的字符串;
5.这个集合类没有泛型;
1.public Object setProperty(String key,String value):作用 : 保存一对属性。 等同于Map中的put功能。
2.public String getProperty(String key):作用 :使用此属性列表中指定的键搜索属性值。等同于Map中的 get(Object key)。
3.public Set
4.public void load(InputStream inStream):作用: 从字节输入流中读取键值对。
特别注意:参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式要求必须是如下格式: key=value
代码使用演示:
- package com.feisi.week6.day5;
-
- import java.io.*;
- import java.util.Properties;
-
- public class Test1 {
- public static void main(String[] args) {
- // 创建一个属性集
- Properties properties = new Properties();
- try {
- // 使用集合对象prop调用load()函数从硬盘上加载数据 文本文件最好使用字符输入流避免截断现象出现
- //properties.load(new FileInputStream("aa.txt"));
- properties.load(new FileReader("aa.txt"));
- // 遍历属性集合中的键与值
- for (String stringPropertyName : properties.stringPropertyNames()) {
- // 打印到控制台
- System.out.println(stringPropertyName + "=" + properties.getProperty(stringPropertyName));
- }
-
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
- }
运行结果:
使用字节输入流导致乱码现象

使用字符输入流正常解析

解析:
1.类加载器:负责将.class文件加载到内存中,并为之生成对应的Class对象,也就是字节码文件对象。
2.Java中有三种类加载器,它们分别用于加载不同种类的class:
2.1引导/启动类加载器(Bootstrap ClassLoader): 用于加载系统类库
\bin目录下的class,例如:Object String 等JDK中的核心类。 2.2扩展类加载器(Extension ClassLoader): 用于加载扩展类库
\lib\ext目录下的class。这些类给JDK自己用的。 2.3应用程序类加载器(Application ClassLoader): 用于加载我们自定义类的加载器。
最后这个类加载器对于我们现在的阶段比较常使用到。
解析:
1.当一个类型加载到内存后就会得到一个对应该类型的Class对象,这个Class对象可以获取用来加载该类的类加载器。
使用下面的方法获得类的加载器:
public ClassLoader getClassLoader():作用:返回加载该类型使用的类加载器。
使用代码演示:
- package com.feisi.week6.day5;
- public class Test5 {
- public static void main(String[] args) {
- // 获取字节码对象
- Student student = new Student();
- // 通过使用getClass方法
- Class extends Student> aClass = student.getClass();
- // 获取类加载器
- ClassLoader classLoader = aClass.getClassLoader();
- System.out.println(classLoader);
-
- Teacher teacher = new Teacher();
- Class extends Teacher> aClass1 = teacher.getClass();
- ClassLoader classLoader1 = aClass1.getClassLoader();
- // 比较两个类加载器是否相同 只要我们自己定义的类,采用的类加载器都是相同的应用类加载器
- System.out.println(classLoader == classLoader1);
-
- // 使用类名.class方式获取字节码对象
- Class
stringClass = String.class; - ClassLoader classLoader2 = stringClass.getClassLoader();
- // String 就是被引导类加载器加载的
- System.out.println(classLoader2);
-
- }
- }
-
- class Teacher{
-
- }
运行结果:

解析:上图展示了 "类加载器"的层次关系,这种关系称为类加载器的"双亲委派模型":
1.双亲委派模型"中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的"父级类加载器"。
2.这种关系不是通过"继承 is a "实现的,通常是通过"组合 has a "实现的。通过"组合"来表示父级类加载器
3."双亲委派模型"的工作过程:
3.1某个"类加载器"收到类加载的请求,它首先不会尝试自己去加载这个类,而是把请求交给父级类加载器。
3.2因此,所有的类加载的请求最终都会传送到顶层的"启动类加载器"中。
3.3如果"父级类加载器"无法加载这个类,然后子级类加载器再去加载。
解析:
1.Java的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如:java.lang.Object,无论哪一个类加载器要加载这个类,最终都是委派给处于顶端的"启动类加载器"进行加载,因此java.lang.Object类在程序的各种类加载器环境中都是同一个类。
2.相反,如果没有"双亲委派机制",如果用户自己编写了一个java.lang.Object,那么当我们编写其它类时,这种隐式的继承使用的将会是用户自己编写的java.lang.Object类,那将变得一片混乱。
3.因此双亲委派机制的一个显而易见的好处是能==够保证类加载的安全性,不会出现因为程序员自己定义Object而出现多个上帝类的现象==。
解析:
1.类加载器主要是用来加载类字节码得到Class对象,除了这个作用外,还可以去加载类路径下【src路径】的资源文件。例如:配置文件
2.类加载器ClassLoad存在一个方法,用来加载src类路径下的文件:
InputStream getResourceAsStream(String name):作用: 返回读取指定资源的输入流。
name是资源文件的路径名,使用相对于src的路径表示文件。特别注意:该方法只能读取相对于src的路径表示文件,否则将报异常。
特别注意:为什么要使用配置文件?解析:使用配置文件是为了在之后的维护过程中减少维护所耗费的时间及工作量。
代码实现演示:
- package com.feisi.week6.day5;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Properties;
-
- public class Test3 {
- public static void main(String[] args) {
- // 创建一个字节码对象
- Class
test3Class = Test3.class; - // 得到类加载器对象
- ClassLoader classLoader = test3Class.getClassLoader();
- // 将配置文件中的数据变成字节流
- InputStream resourceAsStream = classLoader.getResourceAsStream("db.properties");
- // 创建属性集
- Properties properties = new Properties();
-
- try {
- // 使用属性集的load方法将字节流与属性集相关联
- properties.load(resourceAsStream);
-
- System.out.println(properties);
- // 关闭流
- resourceAsStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
运行结果:

解析:
1.在了解反射之前我们先了解两个概念,编译期和运行期:
1.1编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。
解析:在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
1.2运行期是把编译后的文件交给计算机执行,直到程序运行结束。
解析:所谓运行期就把在磁盘中的代码放到内存中执行起来。
2.反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)
3.使用反射操作类成员的前提:要获得该类字节码文件对象,就是Class对象,因此了解Class对象非常重要。
4.反射是框架设计的灵魂,能够让框架更具有通用性,灵活性。
解析:
1.Java中任何一种类型不管是引用数据类型还是基本数据类型都具有其对应的类型对象,即Class对象。
2.Class对象的3中获取方式:
方式1: 通过类名.class获得
方式2:通过对象名.getClass()方法获得
方式3:通过Class类的静态方法获得: static Class forName("类全名")
注意:类全名:包含了包名的类名, 例如:com.feisi.week6.day5.Student每一个类的Class对象都只有一个。因此这三种方式获取的是同一个。
特别注意:Class类没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
代码实际验证:
- package com.feisi.week6.day5;
-
- public class Test6 {
- public static void main(String[] args) throws ClassNotFoundException {
- // 方式1:通过类名.class
- Class
test6Class = Test6.class; - // 方式二:通过对象名.getClass方法
- Test6 test6 = new Test6();
- Class extends Test6> aClass = test6.getClass();
- // 方式三:通过Class类的静态方法
- Class> aClass1 = Class.forName("com.feisi.week6.day5.Test6");
-
- // 比较三个是否为同一个Class对象
- System.out.println(test6Class == aClass);
- System.out.println(aClass1 == aClass);
- System.out.println(test6Class == aClass1);
- }
- }
运行结果:由此结果可知上述的3个Class对象皆为同一个。
解析:
1.使用反射的相关API将一个类型中的构造方法对象,获取,然后按需要操作这个构造方法对象。
1.Constructor类概述
解析:
1.Constructor类是什么?
Constructor提供关于类的单个构造方法的信息以及对它的访问权限。
简单的说,类中的每一个构造方法都是一个Constructor类的对象。
2.反射技术获取构造方法的目的? 获得Constructor对象来创建类的对象。
2.Class类中与Constructor相关的方法
//获取单个指定参数的构造方法
1. Constructor getConstructor(Class... parameterTypes)
* 根据参数类型获得对应的Constructor对象,按照构造方法的参数类型及顺序传入
* 只能获得public修饰的构造方法
public Student(int age,String name){}
举例:
Constructor con=cls.getConstructor(int.class,String.class);
2. Constructor getDeclaredConstructor(Class... parameterTypes)
* 根据参数类型获得对应的Constructor对象
* 可以是public、protected、package-private、private修饰符的构造方法。
//获取所有符合条件的构造方法
3. Constructor[] getConstructors()
获得类中的所有构造方法对象,只能获得public的
4. Constructor[] getDeclaredConstructors()
获得类中的所有构造方法对象
可以是public、protected、package-private、private修饰符的构造方法。
使用注意:要按照你要获取的类中的构造方法的具体声明,来使用不同的获取该类中构造方法的方法,否则将会报异常,上述的方法都是获取目的类中的构造方法。
代码使用演示:
- package com.feisi.week6.day5;
-
- import java.lang.reflect.Constructor;
-
- public class Test7 {
- public static void main(String[] args) throws NoSuchMethodException {
- // 获取Student类中的构造方法
- // 获取该类的Class对象
- Class
studentClass = Student.class; - // 获取Student类中的构造器 也就是构造方法 下面只能获取public所修饰的构造方法
- Constructor
constructor = studentClass.getConstructor(); - System.out.println(constructor);
-
- // 获取私有的构造器 有一个参数
- Constructor
declaredConstructor = studentClass.getDeclaredConstructor(String.class); - System.out.println(declaredConstructor);
-
- }
- }
运行结果:

3.Constructor对象常用方法:
解析:
也就是拿到目的类的构造器之后,使用这些方法创建目的类的实例对象,进而操作目的类的成员。
1.T newInstance(Object... initargs)
根据指定的参数创建对象
2.void setAccessible(true) 如果是非public的构造方法使用,就得先设置可访问
设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消也就是说若我们使用的构造器是私有化的那么我们就使用true来强行使用该构造方法创建目的类的实例化对象。
使用演示:
- package com.feisi.week6.day5;
-
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationTargetException;
-
- public class Test8 {
- public static void main(String[] args) {
- try {
- // 使用反射实例化创建对象
- // 创建Student类的Class对象
- Class
studentClass = Student.class; - // 获取public 构造器
- Constructor
constructor = studentClass.getConstructor(); - // 使用构造器对象创建Student类的实例化对象
- Student student = constructor.newInstance();
- System.out.println(student);
- // 获取使用构造器
- Constructor
declaredConstructor = studentClass.getDeclaredConstructor(String.class); - // 因为是私有的构造器应使设置可访问
- declaredConstructor.setAccessible(true);
- // 使用私有构造器实例化Student类对象
- Student student1 = declaredConstructor.newInstance("张三");
- System.out.println(student1);
-
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
- }
运行结果:

特别注意:以上方式只能用于除抽象类之外的类,否则将会报出InstantiationException异常:为实例化异常;如下图所示:
