文章目录
反射/数据库连接池/IO流
1 Java反射机制
-
什么是反射?
反射又被称为类的“自省”,动态获得类自身的内容(属性,方法,构造器…), 并动态创建类的对象,然后调用这些属性和方法
我们之前编写的代码大多是在编译期定义的,类的对象创建及属性或方法的调用都在编译期已经创建成功
编译期写的代码不具有通用性,如果想要编写出来更加通用,更加灵活的代码,我们则不能在编译器创建对象和调用属性和方法
需要使用反射,在运行期运行期动态创建类的对象并动态调用对象中的属性和方法
- 反射操作的是.class文件而非编译期代码
Java执行过程:.java编译>.class解释>JVM
Java是面向对象的语言,Java中任何元素都是对象,在反射中把Java类中的任何元素都使用对象来进行处理
- 属性是对象:Field
- 方法是对象:Method
- 构造器是对象:Constructor
- 修饰符也是对象:Modifier
- void是对象:Void
1.1 Java反射的基本使用-创建对象及操作类中属性
-
获得类的Class对象
- 由于反射是在运行期工作,所以我们使用反射时需先获取类Class的对象
- Java的类或接口在编译后都存在一个Class对象,我们可以认为Class对象是类的字节码对象
- Class对象表示所有编译后的类或接口
- Java中获取类的Class对象有三种方式
- 通过类的"完整路径字符串"
Class class1=Class.forName("类完整路径字符串");
- 通过类名获取
Class class2=类名.class;
- 通过类的对象名获取
类名 对象名 =new 类名(); Class class3=对象名.getClass();
-
通过类的Class对象获得类的属性
/** * 1. 获得类的Class对象 * 2.通过该类的Class对象获得该类的属性 * 对象名.getFields():获得的属性是访问权限为public的属性(包括static属性) * * 对象名.getDeclaredFields();可或得该类所有方法(含static) * 3.获得类的属性名,修饰符,属性类型 */ public class 通过反射操作类的属性 { public static void main(String[] args) { Class userClass= User.class; // 通过得类的Class对象获得该类的属性 // Field[] userFields = userClass.getFields(); Field[] userFields= userClass.getDeclaredFields(); for (Field field :userFields){ System.out.println(field); //获得属性名 String fieldName =field.getName(); System.out.println(fieldName); //获得属性类型 Class type=field.getType(); System.out.println(type); //获得属性修饰符 int modifierNum =field.getModifiers(); //由于通过getModifiers()获得的是整数形式,我们需要Modifier类这个整数进行解码 String modifier=Modifier.toString(modifierNum); System.out.println(modifier); System.out.println("*******************"); } } }
-
通过属性名获得属性并调用该属性
/** * 1.或得该类Class对象 * 2.或得属性名 * 3.调用该属性 * */ public class 通过属性名获得属性并调用该属性 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Class userClass= User.class; Field username=userClass.getDeclaredField("username"); //反射调用该属性的方法 //1.创建该类对象 Object obj= userClass.newInstance(); /** * 2.设置属性值 *属性名.set(对象名,"属性值") */ username.set(obj,"大王"); /** * 3.调用属性 */ Object username1=username.get(obj); System.out.println(username1); } }
1.2 Java反射的基本使用-操作类中方法及构造器
-
通过反射或得该方法并调用
public class 通过反射或得该方法并调用该方法 { public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Class<User> userClass= User.class; //获得方法对象,根据方法名和参数列表 Method setUserId=userClass.getDeclaredMethod("setUserId", Integer.class); //创建类对象 User user=userClass.newInstance(); //调用该方法 /** * 反射中调用方法 * 方法对象名.invoke(类对象名,方法参数列表) */ setUserId.invoke(user,2); Method getUserId=userClass.getDeclaredMethod("getUserId"); Object userId=getUserId.invoke(user); System.out.println(userId); } }
-
使用反射操作类中的构造方法
public class 通过反射操作类的构造器 { public static void main(String[] args) { Class<User> userClass = User.class; //获得类中构造方法 Constructor[] constructors = userClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { //修饰符 int modifiers = constructor.getModifiers(); String modifier = Modifier.toString(modifiers); //参数列表 Class[] aClass = constructor.getParameterTypes(); System.out.print("参数列表"); for (Class object : aClass) { System.out.print(object+"/"); } //异常 System.out.println(); System.out.print("异常列表:"); Class[] exception = constructor.getExceptionTypes(); for (Class c:exception){ System.out.print(c+" "); } //方法名 String name = constructor.getName(); } } }
-
使用反射调用构造器
public class 获得构造方法并调用 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<User> userClass=User.class; //使用默认构造方法创建对象(JDK11后已过时) // Object o =userClass.newInstance(); //获得无参构造方法 Constructor constructor=userClass.getConstructor(); //使用构造方法创建类的对象 Object o =constructor.newInstance(); //获得有参数的构造方法 Constructor constructor1=userClass.getConstructor(Integer.class,String.class,Integer.class); Object o1=constructor1.newInstance(1,"张三",1); System.out.println(o1); } }
1.3 反射Test
-
赋值器
使用反射编写一个赋值器,通过赋值器将值赋给对象的相关属性中
定义一个方法,参数为Class和Map集合
参数说明:
1.Class 实体类的字节码对象
2.Map集合的key为实体的属性名,value为属性的值
返回值:设置属性的对象/** * 赋值器创建方法 * 1.获得属性名和属性值 * 2.根据属性名拼接set方法 * 3.调用set方法,并赋值 * * @param clas 类的字节码对象 * @param map map集合key是属性名 value是属性值 * @return */ public static <T> T setter(Class<T> clas, Map<String,Object> map) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { T t=clas.getDeclaredConstructor().newInstance(); for (Map.Entry<String,Object> entry : map.entrySet() ){ //获得属性名 String fieldName =entry.getKey(); //获得属性值 Object fieldValue=entry.getValue(); //根据属性名拼接set方法 String methodName ="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1); //创建属性对象 Field field=clas.getDeclaredField(fieldName);//主要用来获得该属性对应的参数类型 //创建方法对象 Method method=clas.getDeclaredMethod(methodName,field.getType()); //调用该方法对象 method.invoke(t, fieldValue); } return t; }
-
封装一个用于request的参数字符的封装,返回Javabean对象
- 定义一个请求参数自动封装的工具类,类中定义一个静态方法,该方法用于将请求的参数字符封装为一个JavaBean对象并返回;
参数说明:
1.Class要封装的类的字节码对象
2.HttpServletRequest 请求对象
返回值:封装好的对象
public static <T> T fenzhuangByRequest(Class<T> clas, HttpServletRequest request) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, ParseException { //获得客户端提交的参数信息,获得一个Map集合key是参数名,value是参数值 Map<String, String[]> map = request.getParameterMap(); //获得类字节码对象 T javabean = clas.getDeclaredConstructor().newInstance(); for (Map.Entry<String, String[]> entry : map.entrySet()) { //判断是否有值 if (entry.getValue() == null || entry.getValue().length == 0) { continue; } //拼接set方法 String methodName = "set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1); //获得属性对象和方法对象 Field field = clas.getDeclaredField(entry.getKey()); Method method = clas.getDeclaredMethod(methodName, field.getType()); //调用该方法 if (field.getType() == String.class) { method.invoke(javabean, entry.getValue()[0]); } else if (field.getType() == Date.class) { Date date = new SimpleDateFormat("yyyy-MM-dd").parse(entry.getValue()[0]); method.invoke(javabean, date); } else if (field.getType() == Integer.class) { method.invoke(javabean,Integer.parseInt(entry.getValue()[0])); }else if (field.getType().isArray()){ method.invoke(javabean, (Object) entry.getValue()); } } return javabean; }
- 定义一个请求参数自动封装的工具类,类中定义一个静态方法,该方法用于将请求的参数字符封装为一个JavaBean对象并返回;
-
使用BeanUtils的类型转换器对日期类型进行处理
/** 方案1:判断属性类型如果是util.Date则进行手动转换 */ if (field.getType() == Date.class) { Date value1 = new SimpleDateFormat("yyyy-MM-dd").parse(entry.getValue()[0]); method.invoke(t, value1); } else { method.invoke(t, ConvertUtils.convert(entry.getValue(), field.getType())); } /** * 方案2:向Convert中注册一个类型转换,该类型转换器BeanUtils中提供的转换器 * * 向ConvertUtils中注册一个转换器,DteLocaleConverter * 参数1:类型转换器对象 * 参数2:转换类型,当需要转换时使用该类型当前注册的类型转换器进行转换 */ ConvertUtils.register(new DateLocaleConverter(Locale.getDefault(), "yyyy-MM-dd"), Date.class); /** * 使用自己的转换器,向Convert中注册 */ ConvertUtils.register(new Converter() { @Override public Object convert(Class aClass, Object o) { if (entry.getValue()[0] == null) { return null; } if (!(entry.getValue()[0] instanceof String)) { throw new ConversionException("只支持字符类转换"); } String str=(String) entry.getValue()[0]; try { return new SimpleDateFormat("yyyy-MM-dd").parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class);
1.4 反射封装查询功能
/**
* 封装查询多行数据功能
*
* @param clas
* @param sql
* @param params
* @return
*/
public List<T> executeQuery(Class<T> clas, String sql, Object... params) {
try {
getConn();
ps = conn.prepareStatement(sql);
if (params != null && params.length != 0) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
rs = ps.executeQuery();
List<T> list = new ArrayList<>();
//获取元数据
ResultSetMetaData metaData = ps.getMetaData();
//获取查询列数
int countNum = metaData.getColumnCount();
while (rs.next()) {
//创建对象
T t = clas.getDeclaredConstructor().newInstance();
for (int i = 0; i < countNum; i++) {
//列名与属性名一致
String fieldName = metaData.getColumnName(i + 1);
//拼接得到set方法
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
//获得属性对象
Field field = clas.getDeclaredField(fieldName);
//获得对应方法对象
Method method = clas.getDeclaredMethod(methodName, field.getType());
//获得一列数据,并将该数据转换为指定的类型
Object value = rs.getObject(i + 1, field.getType());
method.invoke(t, value);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll();
}
return null;
}
/**
* 封装查询单行数据功能
*
* @param clas
* @param sql
* @param params
* @param <T>
* @return
*/
public <T> T executeQueryOne(Class<T> clas, String sql, Object... params) {
try {
getConn();
ps = conn.prepareStatement(sql);
if (params != null && params.length != 0) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData metaData=ps.getMetaData();
//获取列数
int countNum = metaData.getColumnCount();
if (rs.next()){
// 创建类对象
T t=clas.getDeclaredConstructor().newInstance();
for (int i=0;i<countNum;i++){
//获取列名=属性名
String fieldName= metaData.getColumnName(i+1);
//获取方法名
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
//获得属性对象
Field field = clas.getDeclaredField(fieldName);
//获得对应方法对象
Method method = clas.getDeclaredMethod(methodName, field.getType());
//获取列值
Object value =rs.getObject(i+1,field.getType());
method.invoke(t,value);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll();
}
return null;
}
/**
* 通用查询-统计查询
* @param sql SQL语句
* @param params SQL语句中?号占位符的值
* @return 存有查询结果的Bean对象
*/
public Long executeQueryCount(String sql, Object... params){
try {
//连接数据库
this.getConn();
//创建预处理对象
ps = conn.prepareStatement(sql);
//判断SQL语句中是否包含?号占位符(params有值表示有?号占位符,有一个值表示有一个?,有多个值表示有多个?)
if(params!=null && params.length!=0){
//循环设置?号占位符的
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1,params[i]);
}
}
//执行查询语句
rs = ps.executeQuery();
if(rs.next()){
return Long.valueOf(rs.getInt(1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll();
}
return 0L;
}
1.5 使用反射封装servlet[简易用户管理项目]
反射优缺点
-
反射的优点:
反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创和控制任何类的对象,无需提前硬编码目标类
-
反射的缺点
性能问题,使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
2 数据库连接池
传统的数据库连接是通过程序来连接数据库和释放数据库资源的
- 频繁连接和释放会极大浪费系统资源,效率低下
- 实际开发中,都使用的是远程数据库,加上网络时延,频繁连接和关闭数据据库让效率更低
所以我们引入数据库连接池
2.1 数据库连接池概念
数据库连接池是基于池化思想实现的
数据库连接池基本原理
-
在系统创建时自动创建一部分(初始化对象)连接对象与数据库连接
-
当系统中有功能要操作数据库时无需与数据库建立连接,而是从连接池中获得一个空闲连接对象来使用,并将该连接对象标注为繁忙
-
当数据库操作完毕后,断开与连接池连接对象的关联,并将该对象标注为空闲
-
当连接池中连接对象不能满足使用需求时,连接池会自动创建一部分连接对象放到连接池中等待使用
-
当连接池连接对象到达上限后将不在创建新的连接对象
-
如果连接池中的连接对象都被占用,此时新的操作要被放入等待队列中等待连接池中连接对象空闲
-
当连接池存在大量空闲连接时,连接池会释放一部分连接对象
数据库连接池是基于Map集合实现的
有大量开源三方连接池:C3P0连接池,DBCP连接池,Druid连接池
我们使用aibaba的Druid连接池
2.2 基于Druid的数据库连接池技术实现
配置properties
#配置数据库连接属性
#驱动字符串
druid.driverClassName=com.mysql.cj.jdbc.Driver
#数据库连接url
druid.url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
#数据库账号和密码
druid.username=****
druid.password=****
#配置数据库连接池相关属性
#初始化连接数量
druid.initialSize=5
#最大活动连接
druid.maxActive=100
#最小空闲连接
druid.midIdle=1
#最大等待时间
druid.maxWait=1000
private static DruidDataSource dataSource;
/**
* 静态代码块,在类加载时被执行,值执行一次
* 读取db.properties配置文件并初始化数据库连接池
*/
static {
try {
/**
* 1.读取db.properties中的属性文件
* 2.创建Druid数据库连接池对象
*/
//获得输出流对象
InputStream inputStream = DBUntils.class.getClassLoader().getResourceAsStream("db.properties");
//创建properties对象,用于存储db.properties文件中的数据
Properties properties = new Properties();
properties.load(inputStream);
//创建Druid数据库连接对象
//DataSource称为数据源,他是数据的提供者
dataSource =new DruidDataSource();
dataSource.configFromPropety(properties);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获得连接对象
* @return
*/
public Connection getConn() {
try {
conn=dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
2.3 基于Druid配置数据库连接池[简易用户管理项目]
3 IO流
IO流:输入输出流(InputOutput),主要用来操作磁盘中的文件的
-
在Java的java.io包下
-
所有的IO流操作都包含输入输出流
- 输入流:将文件中的内容拆分成一个一个的字节码,然后将这些字节码按照一定的顺序通过IO流管道传递道程序,程序或得这些字节码后,对这些字节码进行组装
- 输出流:将程序中的内容拆分成一个一个字节码,按一定顺序和规则,传递到磁盘文件中吗,并对这些字节码进行组装,组装为具体文件内容
-
IO流的相关操作:
①File类
②字节流
③字符流
④数据流
⑤对象流…
3.1 File类
File类实例表示磁盘中文件或者文件夹
- 通过File的实例可以获取文件或文件的相关属性
- Flie类只能读取或设置文件的相关属性,不能操作文件内容
- 编程中一般文件称为文件,文件夹称为目录
-
File类的构造方法
①
File(File parent, String child)
:根据父文件(parent)创建子文件对象②
File(Sring pathname)
:根据文件路径创建一个文件对象(通常用)③
File(String parent, Sting child)
:根据父文件路径和子文件名创建一个文件对象④
File(URL uri)
:根据URI地址创建文件对象-
File file = new File(“d:/key.pri”)
-
注意:文件路径的分隔符在Windows中"/“反斜杠或”\"双斜杠表示\
为了满足不同操作系统的需要我们可以使用
File.separator
自动根据所在的操作系统生成分隔符
-
-
-
File类的常用方法
(1)
file.exists
:检测file对象指定的文件是否存在(2)
file.createNewFile();
:创建一个新的文件夹(3)
file.getName()
:获取文件名字符串(4)
file.getAbsolutePath()
:获取文件绝对路径字符串(5)
file.getCanonicalPath()
:获取文件规范路径的字符串ps:<先像
file.getAbsolutePath()
获取绝对路径字符串后,然后转成规范路径字符串>(6)
file.getName().substring(file.getName().lastIndexOf(".")+1)
:获取文件扩展名(7)
file.getTotalSpace()
:获取所在磁盘的大小(单位为字节:B)(8)
file.length()
:获得文件大小(单位为字节:B)(9)
file.lastModified()
:获得最后修改时间(以毫秒为单位)(10)
file.delete()
:删除一个文件(11)
file.deleteOnExit()
:JVM运行结束时删除,删除并退出系统(延迟删除)(12)
file.isFile()
:判断是否是文件(13)
file.isDirectory()
:判断是否是目录(14)
file.mkdir()
:创建一级目录(15)
file.mkdirs()
:创建一个多级目录(16)
file.getParent()
:获得文件的父目录名字符串(17)
file.getParentFile()
:获得文件的父目录的对象(18)
file.list()
返回目录下所有子文件的文件名字符串(19)
file.listFiles()
获得目录下所有子文件的File对象(20)
file.listFiles(FileFilter fileFilter)
:使用文件过滤接口获得子文件中所有指定的文件(21)
File.listRoots()
:获得当前系统中的所有盘符文件,返回File数组
-
问题1:编程实现创建"d:/abc/xyz/hello.java"目录及文件
-
问题2:编程实现删除"d:/abc/xyz/hello.java"目录及文件
-
问题3:扫描磁盘的所有文件
package com.jiazhong.IO流.File类; import java.io.File; /** * 扫描指定目录的所有文件 * 使用递归调用 */ public class Demo04 { public static void main(String[] args) { File[] roots = File.listRoots();//获得当前电脑所有磁盘 for (File file:roots){ scannerFile(file); } } /** * 扫描某个路径所有文件 * @param file */ static void scannerFile(File file){ if (file==null){//文件无效,退出本次循环 return; } if (file.isFile()){//如果是文件输出文件地址,退出本次调用 String filePath=file.getAbsolutePath(); System.out.println(filePath); return; } if (file.isDirectory()){ //获得当前目录所有子文件 File[] files = file.listFiles(); if (files!=null&&files.length!=0){ //遍历子文件 for (File file1:files){ scannerFile(file1); } } } } }
3.2 字节输入输出流(InPutStream/OutPutStream)
字节流是以字节码的方式传输数据,他是所有流的基础,其他流都是在字节流的基础上进行操作
字节流提供了InPutStream/OutPutStream两个基础流,但这两个流都是抽象类,是字节流的基类
我们在使用字节输入输出流一般使用FileInPutStream和FileOutOutStream这两个流来实现基于字节流的输入和输出操作
字节流可以操作文本文件但是不建议,因为字节流操作的是字节,很容易乱码,经常用来操作二进制文件
带有缓冲区的字节流,BufferedInputStream和BufferedOutputStream,这两个流有默认缓冲区,可以提高读取效率
FileInPutStream和FileOutOutStream是节点流(原始流)
BufferedInputStream和BufferedOutputStream(处理流),针对原始流进行处理的
- BufferedInputStream和BufferedOutputStream两个类存在一个默认缓冲区,大小为8K,如果自定义缓冲区大于8k则用自定义的,反之则用磨人的
外部流关闭内部流自动关闭
- 字节输入流
/**
* 字节输入流实例
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("d:/hello java.txt");
//读取文件内容
//读取一个字节
/* int num=inputStream.read();
System.out.println((char)num);
*/
//一次读取多个字节,组装到字节数组中
byte[] buf = new byte[1024];
int len = -1;
// 将读取到的字节存入到buf中,返回值实际读到的内容
while ((len = inputStream.read(buf)) != -1) {
String str = new String(buf,0,len);//从下标为0开始到下标为len结束创建字符串
System.out.println(str);
}
inputStream.close();
}
}
-
字节输出流
字节输出流在向目标文件输出内容时,如果目标文件不存在,创建文件写入内容,如果存在,则更新内容
若不想更新,FileInputStream对象的第二个参数指定为true
OutputStream outputStream=new FileOutputStream("D:/hello.txt",true);
/** * 字节输出流 */ public class Demo02 { public static void main(String[] args) throws IOException { OutputStream outputStream=new FileOutputStream("D:/hello.txt",true); Scanner scanner=new Scanner(System.in); System.out.println("请输入要输出的内容,输出N时结束:"); Boolean flag=false; while (!flag){ String str=scanner.nextLine();//输出一行数据 if (str.equals("N")||str.equals("n")){ break; } //将一个字符串转化为字节数组 byte[] bytes = str.getBytes(); outputStream.write(bytes); outputStream.write("\n".getBytes()); outputStream.flush();//刷新流 } outputStream.close();//关闭流 } }
- 文件复制
/** * 文件复制 * @param src 源文件 * @param desc 目标文件 * @throws FileNotFoundException */ public static void fileCopy(File src,File desc) throws IOException { InputStream inputStream=new FileInputStream(src); OutputStream outputStream=new FileOutputStream(desc,true); byte[] buf = new byte[1024*1024*10]; int len=-1; while ((len=inputStream.read(buf))!=-1){ outputStream.write(buf,0,len); outputStream.flush(); } inputStream.close(); outputStream.close(); }
3.3 文件编码方式
① ISO-8859-1:西方编码,智能识别西方的语言
②GBK:中国标准编码,可以识别简体和繁体中文,一个字符占2个字节
③GB2312:中国简体标准编码,智能识别简体中文,一个字符占2个字节
④UTF-8:国际编码,可以识别几乎所有国家的语言,一个字符占3个字节
⑤Unicode:java所用编码
⑥ANSI:操作系统编码,我们国内使用的WindowsOS都是GBK或GB2312
-
我们操作文件时,由于外部文件编码方式和Java所用编码(Unicode)不统一,所以要进行转码
-
将外部文件编码转换为Java编码,我们称为解码
String str=new String(buf,o,len,"GBK")
将外部文件编码"GBK"转化为Java编码 -
将Java编码转换为外部文件编码,我们称为编码
byte[] buf=str.getBytes("GBK")
“将Java编码转化为外部编码”
-
3.4 桥接流(字节到字符的转换流)
字节到字符的转换流属于处理流
字节流的底层就是通过"字节流"读取原始数据,然后再将这些读取到的"字节数据"转换为"字符数据"
字节到字符的转换流是按照外部文件编码,将字节数据转化为字符数据,并指定外部编码
字节到字符的转换流是通过具体字符流的基础
字节到字符的转换流:InputStreamReader和OutPutStreamWrite这两个类来实现
InputStreamReader:是字节到字符的输入流,他继承Reader类
OutPutStreamWrite:是字节到字符的输出流,他继承Write类
Reader类:是一个抽象类,是字符输入流的基类
Write类:是一个抽象类,是字符输出流的基类
- 使用字节到字符的转换流可以根据外部文件编码设置转换编码
- 字节到字符输入流
/**
* 字节到字符输入流
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\hello java.txt");
//该流会将读到的字节转换为字符,并根据指定的编码进行转换
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"GBK");
char[] buf = new char[1024];
int len = -1;
while ((len = inputStreamReader.read(buf)) != -1) {
String str=new String(buf,0,len);
System.out.println(str);
}
inputStream.close();
}
}
- 字节到字符的输出流
/**
* 字节到字符输入流
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\hello java.txt");
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"GBK");
char[] buf = new char[1024];
int len = -1;
while ((len = inputStreamReader.read(buf)) != -1) {
String str=new String(buf,0,len);
System.out.println(str);
}
inputStream.close();
}
}
3.5 字符流
字符流包含FileReader和FileWrite两个类,他们分别继承InputStreamReader和OutputStream这两个转换源
FileWrite和FileReader是InputStreamReader和OutputStream的的简洁实现,这两个字符流类将编码固化,编码统一使用UTF-8
- 在FileReader和FileWriter中无法设置编码只能使用UTF-8编码进行转换,所以只能操作编码为UTF-8的文本文件
- JDK11后FileReader和FileWriter支持设置编码集
- BufferedReader和BufferedWriter这两个类内部带有缓冲区,效率会适当提高,但我们使用这两流不是为了提高效率,而是这两个类中提高操作字符数据的便捷方法
- BufferedReader和BufferedWriter属于包装流,对Reader和Writer子类对象的包装,我们在使用时可以使用FileReader和FileWriter做为基础流
也可以使用InputStreamReader和OutputStreamWriter做为基础流
- 字符输入流
/**
* 字符输入流
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
Reader reader=new FileReader("d:/hello java.txt");
char[] buf=new char[1024];
int len=-1;
while ((len=reader.read(buf))!=-1){
String str=new String(buf,0,len);
System.out.println(str);
}
reader.close();
}
}
- 字符输出流
public class Demo01 {
public static void main(String[] args) throws IOException {
Writer writer=new FileWriter("d:/hello.txt");
writer.write("你好");
writer.flush();
writer.close();
}
}
- 字符输入输出缓冲流
public class BufferDemo {
public static void main(String[] args) throws IOException {
// bufferReader();
bufferWrite();
}
/**
* 缓冲字符输出流
* @throws IOException
*/
private static void bufferWrite() throws IOException {
Writer writer=new FileWriter("d:/hello.txt");
BufferedWriter bufferedWriter=new BufferedWriter(writer);
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
bufferedWriter.write(str);
bufferedWriter.newLine();//输出一个换行符
writer.flush();
writer.close();
}
/**
* 缓冲输入流
*/
static void bufferReader() throws IOException {
/**
Reader reader=new FileReader("d:/hello java.txt");
BufferedReader bufferedReader=new BufferedReader(reader);
*/
//创建字节输入流对象
InputStream inputStream=new FileInputStream("d:/hello java.txt");
//创建字节到字符流对象
InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
//创建字符缓冲流对象
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
String len=null;
while ((len=bufferedReader.readLine())!=null){
System.out.println(len);
}
inputStream.close();
}
}
3.6 打印流和数据流
-
打印流
Printwriter
:向目标位置写入文本public class Demo01 { public static void main(String[] args) throws IOException { //控制台输入 // Scanner scanner=new Scanner(System.in); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); bufferedReader.read(); //控制台输出 PrintWriter printWriter = new PrintWriter(System.out); while (true) { String s = bufferedReader.readLine(); printWriter.write(s); if (s.equals("n")) { break; } printWriter.flush(); } bufferedReader.close(); printWriter.close(); } }
-
数据流
使用该流可以对基本数据类型输入和输出
可以根据数据流读取任意基本数据类型数据
数据流根据所要操作的数据类型所占的字节数,一次性读取或写出对应字节数,以达到操作基本数据类型数据的精准性和效率
数据流由两个类组成(DataInputStream/DataOutPutStream)
public class Demo01 { public static void main(String[] args) throws IOException { dataOutputStream(); dataInputStream(); } /** * 数据输出流 * @throws IOException */ private static void dataOutputStream() throws IOException { DataOutputStream dataOutputStream=new DataOutputStream(new FileOutputStream("D:/hello.num")); dataOutputStream.writeInt(256); dataOutputStream.writeInt(5256); dataOutputStream.writeInt(2256); dataOutputStream.writeInt(2526); dataOutputStream.writeDouble(2562.06); dataOutputStream.flush(); dataOutputStream.close(); } /** * 数据输入流 * @throws IOException */ public static void dataInputStream() throws IOException { DataInputStream dataInputStream=new DataInputStream(new FileInputStream("d:/hello.num")); try { while (true){ int num = dataInputStream.readInt(); System.out.println(num); } }catch (EOFException e){ System.out.println("文件读取结束"); } dataInputStream.close(); } }
3.7 对象流/序列化和反序列化
对象流主要用来操作Java对象,将Java对象写出到文件中,或从文件中读取一个java对象
将一个Java对象写入到磁盘中或网络中称为序列化
将文件或网络中的数据转化Java对象中称为反序列化
序列化:将一个Java对象转化为字节码的过程(流化)
反序列化:将字节码转化为Java对象的过程ObjectInputStream:对象输入流/ObjectOutputStream对象输出流
ObjectOutputStream:实现对象序列化
ObjectInputStream:实现对象反序列化Java规定只有实现了接口(Serializable)的对象才能进行序列化和反序列化操作
序列化接口(Serializable)中没有定义任何属性和方法,他是一个标识接口(做标识使用)
当对一个Java对象进行序列化操作时,JVM会检测该对象所属的类是否实现了Serializable接口,如果没有实现则JVM会阻止该对象序列化
反之如果对象实现了Serializable接口,则允许其序列化
一个集合类对象,里面的元素要想被序列化也必须实现Serializable接口
一个被序列化的对象存在另一个类的属性,则该属性要想被序列化也必须其对应的类也必须实现Serializable接口
某个属性不能被序列化时我们用trainsient修饰,即不允许其序列化,即在序列化时将标有trainsient的属性特殊处理不让其序列化。注意的是:一个静态属性无论是否有trainsient修饰都不能被序列化
package com.jiazhong.IO流.对象流;
import com.jiazhong.IO流.对象流.model.User;
import java.io.*;
public class ObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// ObjOutputStream();
ObjInputStream();
}
/**
* 将Java对象存储到磁盘中
*
* @throws IOException
*/
public static void ObjOutputStream() throws IOException {
User user = new User(2, "校长", "123456");
User user1 = new User(1, "校长", "123456");
User user2 = new User(3, "校答长", "123456");
User user3 = new User(5, "校按时长", "123456");
User user4 = new User(4, "校达个长", "123456");
User user5=new User(6,"dasd","123456","男");
//创建对象输出流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/user.obj"));
objectOutputStream.writeObject(user);
objectOutputStream.writeObject(user1);
objectOutputStream.writeObject(user2);
objectOutputStream.writeObject(user3);
objectOutputStream.writeObject(user4);
objectOutputStream.writeObject(user5);
objectOutputStream.flush();
objectOutputStream.close();
}
/**
* 从磁盘中读取数据并转化为Java对象
*/
public static void ObjInputStream() throws IOException, ClassNotFoundException {
//创建对象输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/user.obj"));
try {
while (true) {
User user = (User) objectInputStream.readObject();
System.out.println(user);
}
} catch (IOException e) {
System.out.println("已读完");
e.printStackTrace();
}
objectInputStream.close();
}
}
- 学习来自于西安加中实训