资源描述
《输入输出和反射 》教案
第三章 本章题目
1 . 回顾上一章:[10分钟]………………………………………………………………………..…..…….…340
2 . 课程知识点讲解………………………………………………………………………………..……………340
2.1.具体知识点1:[XX分钟]……………………………………………………………………………….340
2.2.具体知识点2.:[XX分钟]………………………………………………………………………………340
2.3.具体知识点3:[XX分钟]………………………………………………………………………………..341
3 . 本章总结 [10分钟]…………………………………………………………………….……………………341
4 . 考核点………………………………………………………………………………………………………..341
5 . 扩展部分………………………………………………………………………………….…………………..341
6 . 学员问题汇总…………………………………………………………………………….……………..........341
7 . 作业………………………………………………………………………………………….………………..341
授课教师:XXX
课时:2小时
本章主要目的
XXX
本章重点
XXX
本章难点
XXX
1 、预习检查:[X分钟]
· 什么是流?
· 简述使用字节流读文本文件的步骤
· 使用什么流读写字符内容更高效?
2 、课程知识讲解:
2.1 具体知识点1:File类 [30分钟]
· 什么是文件?
文件可认为是相关记录或放在一起的数据的集合
· 文件一般存储在哪里?
磁盘,硬盘 软盘 U盘等
· JAVA程序如何访问文件属性?
JAVA API :java.io.File 类 操作文件或目录的属性
老师提问
问题:File类是如何操作文件或目录的属性?
· File类访问文件属性
a.通过File类,创建File对象 File file = new File( String pathname ); pathname文件在磁盘中的目录
b.通过文件对象的方法操作文件或目录属性
注意:文件路径中的分隔符可以是正斜杠 / 也可以是反斜杠 \,但是反斜杠必须是\\
File.separator 与系统有关的默认名称分隔符。此字段被初始化为包含系统属性 file.separator 值的第一个字符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\'。
File类的常用方法:(参考API)
通过案例讲解集中常用的方法
//boolean delete( ) 删除此对象指定的文件或目录
//boolean createNewFile( ) 创建名称的空文件不创建文件夹,如果已经存在就不能建
//获取文件的名字
System.out.println("文件的名字:"+file.getName());
System.out.println("文件的相对路径:"+file1.getPath());
//获取绝对路径
System.out.println("绝对路径:"+file.getAbsolutePath());
//获得文件最后修改的时间
Calendar c=Calendar.getInstance();
c.setTimeInMillis(file.lastModified());
Date date=c.getTime();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("文件最后修改的时间:"+sdf.format(date));
//获得文件的大小
System.out.println("文件的大小:"+file.length()+"Kb");
老师提问
问题: 如何读写文件?
分析:java中的File类只能操作文件或目录的属性,不能操作文件里面的数据。
读文件:是指把文件中的数据读取到内存中。写文件:就是把内存中的数据写入到文件中。在java中是通过流来读写文件的。
2.2 具体知识点2:流 [XX分钟]
知识点讲解1:流的定义
· 通过流来读写文件
· 流是指一连串流动的字符,是以先进先出方式发送信息的通道
用生活中的例子(比如水管流水)
知识点讲解2:流的分类
流分为输入流和输出流。输入输出是相对计算机内存说的,如果数据是输入到内存,则称为输入流,如果是从内存中输出,则称为输出流。
数据源与流的关系:数据源分为源数据源和目标数据源 输入流联系的是源数据源 ,输出流联系的是目标数据源。
详见PPT12 PPT13
知识点讲解:文件的读写
· 文本文件的读写
· 用FileInputStream和FileOutputStream读写文本文件
· 用BufferedReader和BufferedWriter读写文本文件
· 二进制文件的读写
· 使用DataInputStream和DataOutputStream读写二进制文件 (和文本文件的读写类似不详细的讲解)
· 使用FileInputStream 读文本文件的步骤是什么? (对照API讲解FileInputStream的方法)
FileInputStream就是将文件中的数据输入到内存中,它是InputStream抽象类的子类,用来读取文件中的数据。
① 引入相关的类 (相关的jar包)
② 构造文件输入创建流FileInputStream 对象
FileInputStream fis= new FileInputStream("text.txt");
③ 读取文本文件的数据
fis.available(); //获得文件的字节数
fis.read();
④ 关闭文件流对象 fis.close();
案例讲解:
public static void main(String[] args) throws Exception {
File file=new File("D:\\test.txt");
FileInputStream fis=new FileInputStream(file); //2步
System.out.println("可读取的字节数:"+fis.available());
System.out.println("文件内容是:");
int date;
while((date=fis.read())!=-1){ //3步
//强制类型转换 如果不强转 那么就是对呀的ASCII码
System.out.println((char)date+" ");
}
fis.close();
}
或者
public static void main(String[] args) throws Exception {
File file=new File("D:\\test.txt");
FileInputStream fis=new FileInputStream(file);//2
System.out.println("可读取的字节数:"+fis.available());
System.out.println("文件内容是:");
byte[] buffer=new byte[fis.available()];//创建合适文件大小的数组
fis.read(buffer); //读取文件中的内容到b[]数组
fis.close();
//String构造 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
System.out.println(new String(buffer));
}
· 使用FileOutputStream 写文本文件的步骤是什么?与读文件的步骤有何不同?
FileOutputStream就是将内存中的数据输出到文件中,它是FileOutputStream抽象类的子类,把内存的数据写入到文本文件中。
② 引入相关的类 (相关的jar包)
② 构造文件输出创建流FileInputStream 对象
FileOutputStream fos = new FileOutputStream(“text.txt");
③ 把数据写入文本文件
String str ="好好学习Java";
byte[] words = str.getBytes();
fos.write(words, 0, words.length);
④ 关闭文件流对象 fos.close();
案例:
public static void main(String[] args) throws Exception {
//创建输出流对象
FileOutputStream fos=new FileOutputStream("d:\\123.txt",true);
//把数据写入文本文件
String str="asdasdasfewfwe";
byte[] b=str.getBytes();
fos.write(b);
fos.flush();//刷新
fos.close();//关闭流
System.out.println("数据写入完成!!!");
}
注意:FileOutputStream (File file) 和 FileOutputStream(String name) 两种构造方法在向文件写数据时将覆盖文件中原有的内容,如果是不覆盖原来的内容,直接写在原来的文件内容的后面就需要用到第三种方法FileOutputStream(String name,boolean append) true表示不覆盖
创建FileOutputStream实例时,如果相应的文件并不存在,则会自动创建一个空的文件
案例:复制一个文件的内容到另外一个文件
public static void main(String[] args) throws Exception {
//创建输入流对象对文件进行读取
FileInputStream fis=new FileInputStream("D:\\student.txt");
//创建输出流对象,将读取到的文件写入到另外的文件中
FileOutputStream fos=new FileOutputStream("E:\\studentcopy.txt");
//创建中转的数组,将读取的信息存到数组中
byte[] buffer=new byte[1024];
//循环读取数据,并写入到文件中
// read()下一个数据字节;如果已到达文件末尾,则返回 -1。
System.out.println("开始赋值.......");
while(fis.read()!=-1){
fis.read(buffer);
fos.write(buffer);
fos.flush();
}
fos.close();
fis.close();
System.out.println("复制结束.......");
}
用 字符流类BufferedReader和Reader 读文本文件 (结合API帮助文档讲解)
BufferedReader和Reader都是Reader抽象类的子类,他们可以通过字符流的方式读取文件,并且使用缓冲区(它可以先把一批数据读到缓冲区,接下来的读操作都是从缓冲区内获取数据,避免每次都从数据源读取数据进行字符编码转换,从而提高读取操作的效率),提高了文件读取的效率。
按行读取内容的readLine()方法 一行一行的读取。
步骤和FileInputStream类似
Reader in = null;
BufferedReader bf = null;
try {
in = new FileReader("D:\\test.txt");
bf = new BufferedReader(in);
// 读取有一行数据
String line = bf.readLine();
while (line != null) {
System.out.println(line);
line = bf.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally { //关闭流
try {
if (bf != null) {
bf.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
用 字符流类BufferedWriter和Writer写文本文件 (结合API帮助文档讲解)
将内存中的数据写入到文本文件中。BufferedWriter带有缓冲区。
BufferedWriter类的缓冲区(BufferedWriter与BufferedReader的流方向正好相反,BufferedWriter是把一批数据写到缓冲区,当缓冲区满的时候,再把缓冲区的数据写到字符输出流中。这可以避免每次都执行物理写操作,从而提高io操作的效率。
bw.newLine();//写入一个行分隔符。 换行
bw.write(str); // 写数据
案例:
public static void main(String[] args) {
//创建writer对象
Writer writer=null;
//创建BufferWriter对象
BufferedWriter bw=null;
String str="好好学习,天天向上!!!!!";
try {
writer=new FileWriter("D:\\234.txt",true);
bw=new BufferedWriter(writer);
bw.newLine(); //写入一个行分隔符。
bw.write(str);
bw.flush();//刷新缓冲区
} catch (IOException e) { //关闭流
e.printStackTrace();
}finally{
try {
if (writer!=null) {//判断流是否是空
writer.close();
}
if (bw!=null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("数据写入完成!!!");
}
老师提问:
问题1:读取包含中文的文本文件时,可能出现中文乱码,怎么办?
分析:java为我们提供了一个类:InputStreamReader,继承Reader类,使用InputStreamReader并设置编码格式(演示乱码出现的过程和解决方案)
public static void main(String[] args) throws Exception {
File file=new File("D:\\1234.txt");
FileInputStream fis=new FileInputStream(file);
////使用InputStreamReader并设置编码格式
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(isr);
String line=br.readLine();
while(line!=null){
System.out.println(line);
line=br.readLine();
}
br.close();
isr.close();
fis.close();
}
案例:使用BufferedReader和BufferedWriter实现文件的拷贝
public static void main(String[] args) throws Exception {
// 创建FileReader对象
FileReader fr = null;
// 创建bufferedReader对象
BufferedReader br = null;
// 创建FileWriter对象
FileWriter fw = null;
// 创建BufferedWriter对象
BufferedWriter bw = null;
try {
fr = new FileReader("D:\\123.txt");
br = new BufferedReader(fr);
fw = new FileWriter("E:\\copy.txt");
bw = new BufferedWriter(fw);
System.out.print("开始复制文件");
String line = null;
while ((line = br.readLine()) != null) {
System.out.print(".");
bw.write(line);
bw.newLine();
bw.flush();
}
System.out.println();
System.out.println("文件复制完成...");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bw != null) {
bw.close();
}
if (fw != null) {
fw.close();
}
if (br != null) {
br.close();
}
if (bw != null) {
bw.close();
}
}
}
2.3 具体知识点3:序列化和反序列化[20分钟]
为什么要使用序列化和反序列化?(举例讲解)
当两个进程在进行远程通信时(QQ通讯),彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
序列化:就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或传输格式的过程,在序列化过程中,会将对象的公有成员、私有成员包括类名,转换为字节流,然后再把自己流写入数据流,存储到介质中,这里说的介质就是指文件。
反序列化:顾名思义就是与序列化相反,反序列化就是从特定的存储介质中讲数据重新构建对象的过程。(也就是将序列化的对象还原)
知识讲解1:序列化的步骤:(四步)
① 需要序列化的类实现Serializable接口
② 创建对象输出流
FileOutputStream os=new FileOutputStream(f);
//ObjectOutputStream 核心类
ObjectOutputStream oos=new ObjectOutputStream(os);
③ 调用writeObject()方法将对象写入文件
④ 关闭对象输出流
案例
public static void main(String[] args) throws Exception {
//实例化User对象
User user=new User("张三",26,"java12");
//创建对象输出流
FileOutputStream fos=new FileOutputStream("D:\\user.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
//用输出流的writer()方法将对象写入文件
oos.writeObject(user);
oos.close();
fos.close();
System.out.println("序列化完毕!!");
}
注意:可以使用集合保存对象,然后将集合中所有的对象进行序列化
知识讲解2:反序列化的步骤:(四步)
① 需要序列化的类实现Serializable接口
② 创建对象输入流
InputStream is=new FileInputStream(f);
//ObjectOutputStream 核心类
ObjectInputStream ois=new ObjectInputStream(is);
③ 调用readObject ()方法读取对象
④ 关闭对象输出流
案例:
public static void main(String[] args) throws Exception {
//创建对象输入流
FileInputStream fis=new FileInputStream("D:user.txt");
ObjectInputStream ois=new ObjectInputStream(fis);
//调用readObject ()方法读取对象 返回值是objec类型,要强转
User user=(User)ois.readObject();
//关闭流
ois.close();
fis.close(); System.out.println(user.getName()+"\t"+user.getAge()+"\t"+user.getClazz());
}
//反序列化list集合 返回值是objec类型,要强转
FileInputStream fis=new FileInputStream("D:users.txt");
ObjectInputStream ois=new ObjectInputStream(fis);
//调用readObject ()方法读取对象 返回值是objec类型,要强转
ArrayList<User> list=(ArrayList<User>) ois.readObject();
for (User user : list) {
System.out.println(user.getName()+"\t"+user.getAge()+"\t"+user.getClazz());
}
//关闭流
ois.close();
fis.close();
补充知识讲解:对于一些敏感的信息,如用户的密码,银行卡等,一旦被序列化后,人们可以通过读取文件或者拦截网络传输的数据方式来获取这些信息。那么为了安全,我们可不可以对一些特殊的字段不进行序列化呢?java为我们提供了transient来修饰,可以使得该属性不被序列化
对象引用的序列化:
在一个类的成员包含其他类时,如班级类中包含学生类型的对象,那么要序列化班级对象时,则必须保证班级类和学生类都可以被序列化。即序列化某个对象时,他的各个成员对象也必须是可序列化的。
2.4具体知识点4:反射机制 [20分钟]
知识点讲解1:什么是反射?
Java反射机制是java的特性之一,反射机制是构建框架的基础所在。Java反射机制是指在运行的状态中,动态的获取信息以及动态调用对象方法的功能。有三个动态特征:
1. 运行时生成对象实例 2.运行期间调用方法3.运行时更改属性
如何理解反射的原理?首先我们来回顾一下Java程序中的执行过程,如图所示,想要java程序能够运行,java类就需要被java虚拟机加载,运行的程序都是在编译的时候就加载了所有的类。
Java反射机制在编译的时候并不确定是哪个类被加载,而是在程序运行的时候才加载,探知,和使用,如图所示,这样的特点就是反射。
反射机制能够知道类的基本结构、类的属性、方法,例如MyEclipse的自动提示功能就是利用了java的反射原理,对所创建对先个探知和自审。
Java反射机制的功能
1. 在运行时判断任意一个对象所属的类
2. 在运行时构造任意一个类的对象
3. 在运行时判断任意一个类所具有的方法和属性
4. 在运行时调用任何一个对象的方法
知识讲解2:反射技术常用API
· 反射常用的Java 类型 位于java.lang.reflect包中
· Class类—可获取类和类的成员信息
· Field类—可访问类的属性
· Method类—可调用类的方法
· Constructor类—可调用类的构造方法
· 使用反射的基本步骤(四步)
· 1.导入java.lang.reflect.*
· 2.获得需要操作的类的Java.lang.Class对象
· 3.调用Class的方法获取Field、Method等对象
· 4.使用反射API进行操作 (设置属性﹑调用方法)
· Class类是Java 反射机制的起源和入口
· 每个类都有自己的Class 对象
· 用于获取与类相关的各种信息
· 提供了获取类信息的相关方法
· Class类继承自Object类
通过反射获取类的信息分为两步:首先获取Class对象,然后通过Class对象获取信息。
1.获取Class对象
每个类被加载后,系统会为该类生成一个对应的Class对象,通过该对象就可以访问到Java虚拟机中的这个类。获得Class对象的方法有3中:
a. 通过对象的getClass()方法
User user =new User(); //自定义的User类
Class clazz=user.getClass(); //clazz为Class的对象
b. 调用类的class属性
Class clazz=User.class;//自定义的User类
c. 使用Class类的forName()静态方法
Class clazz=Class.forName("com.pb.entity.User");//传入字符串是类的全名
错误的写法:Class clazz=Class.forName("User");//传入字符串是类的全名
2. 通过Class对象获取信息
获取Field 对象
Field fields[ ] = clazz.getDeclaredFields();
//获得所得属性
Field [] fields=clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
获取Method 对象
Method methods[] = clazz.getDeclaredMethods();
//获得所有的方法
Method [] methods=clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName()+"\t\t"+method.getReturnType());
}
获取Constructor对象
Constructor constructors[ ] = clazz.getDeclaredConstructors();
//获得所有的构造方法
Constructor constructors[] = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
使用反射动态创建对象
· 方法一:使用Class的newInstance()方法,仅适用于无参构造方法
Class clazz=Class.forName("com.pb.Student");
Object obj=clazz.newInstance(); //相当于Student stu = new Student();
案例:
Class clazz=Date.class;
Date d=(Date)clazz.newInstance();
System.out.println(d.toString());
new Instance()调用的是无参构造,如果该类没有无参构造方法,则newInstance()会产生异常.
· 方法二:调用Constructor的newInstance()方法,适用所有构造方法
Constructor cons = clazz.getConstructor(new Class[ ]
{ String.class,int.class, float.class });
Object obj = cons.newInstance(new Object[ ] { "lkl", 32, 56.5f });
相当于:Student stu=new Student("lkl",32,56.5f);
案例:
Class clazz=Date.class;
Constructor c=clazz.getConstructor(long.class);
Date d=(Date)c.newInstance(2014);
System.out.println(d.toString());
· 调用方法基本步骤
· 1.通过Class对象获取Method 对象
· 2.调用Method对象的invoke()方法
案例:
//创建该类的对象
Class clazz=Class.forName("com.pb.reflect.Student");
Object obj=clazz.newInstance();
//调用显示学生信息的方法 new Class[]{String.class,String.class,int.class}方法的参数
Method method=clazz.getMethod("show", new Class[]{String.class,String.class,int.class});
Object o=method.invoke(obj, new Object[]{"张三","男",22});
//调用构造方法
Constructor c=clazz.getConstructor(new Class[]{String.class,String.class,int.class});
Object obj2=c.newInstance(new Object[]{"李四","java13",23});
//创建该类的对象
Method method1=clazz.getMethod("show",null);
Object o2=method1.invoke(obj2, null);
反射创建数组:
还可以通过反射来动态的创建数组、操作数组中的元素。
public static void main(String[] args) {
//创建一个元素类型为String,长度为10 的数组
Object obj=Array.newInstance(String.class, 10);
//一次为数组赋值
Array.set(obj, 0, "张三");
Array.set(obj, 1, "张三1");
Array.set(obj, 2, "张三2");
Array.set(obj, 3, "张三3");
//一次取出数组中的元素
Object o1=Array.get(obj, 0);
System.out.println(o1);
}
3、本章总结 [10分钟]
· 简述流的分类。
· 读写文件常用的类有哪些?
· 简述序列化和反序列化的步骤。
· 通过反射创建对象有几种方式,如何实现?
4、考核点
考核点1:
考核点2:
5、扩展部分:
扩展部分1:
扩展部分2:
扩展部分3:
6、学员问题汇总:
学员问题1:
学员问题2:
7、作业:
习题1:
习题2:
习题3:
展开阅读全文