JAVA基础(九)
文件:文件是保存数据的地方。
文件流:文件在程序中是以流的形式来操作的。
流:数据在数据源(文件)和程序(内存)之间经历的路径。
输入流:数据从数据源(文件)到程序(内存)的路径。
输出流:数据从程序(内存)到数据源(文件)的路径。
创建文件对象相关构造器和方法:
new File(String pathname)//根据路径构建一个File对象
new File(File parent,String child)//根据父目录文件+子路径构建
new File(String parent,String child)//根据父目录+子路径构建
public void info(){
File file2 = new File("e:\\new1.txt");
System.out.println("文件名 = " + file2.getName());
System.out.println("文件绝对路径 = " + file2.getAbsolutePath());
System.out.println("文件父级目录 = " + file2.getParent());
System.out.println("文件大小(字节) = " + file2.length());
System.out.println("文件是否存在 = " + file2.exists());
System.out.println("是不是一个文件 = " + file2.isFile());
System.out.println("是不是一个目录 = " + file2.isDirectory());
}
目录的操作和文件删除:
mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件
IO流原理及流的分类:
- I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
- Java程序中,对于数据的输入/输出操作以"流(Stream)"的方式进行。
- java.io包提供了各种"流"类和接口,用以获取不同类型的数据,并通过方法输入或输出数据。
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
流的分类:
-
按操作数据单位不同分为:字节流(8bit),字符流(按字符)
-
按数据流的流向不同分为:输入流、输出流
-
按流的角色不同分为:节点流、处理流/包装流。
-
(抽象基类) 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer (1) Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。
(2)由这四个类派生出来的子类名称作为子类名后缀。
FileReader和FileWriter:
FileReader相关方法:
- new FileReader(File/String)
- read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
- read(char[]):批量读取多个字符到数组,返回读取到字符数,如果到问价末尾返回-1
相关API:
- new String (char []):将char[] 转换成String
- new String(char[]off,len):将char[]的指定部分转换成String
FileWriter常用方法:
- new FileWriter(File/String):覆盖模式,相当于流的指针在首端
- new FileSriter(File/String,true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char []):写入指定数组
- write(char[],off,len):写入指定数组的指定部分
- write(String):写入整个字符串
- write(String,off,len):写入字符串的指定部分
- 相关API:String类:toCharArray:将String转换成char[]
注:FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件。
节点流和处理流:
- 节点流可以从一个特定的数据源读取数据,如FileReader、FileWriter
- 处理流(也叫包装流)是"连接"在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferedWriter。
区别:
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
- 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
处理流的功能主要体现在以下两个方面:
- 性能的提高:主要以增加缓存的方式来提高输入输出的效率。
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
BufferedInputStream是字节流,在创建BufferedInputStream时,会创建一个内部缓冲区数组。
BufferedOutStream是字节流,实现缓存的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统。
对象流-ObjectInputStream 和ObjectOutputStream
序列号和反序列化:
- 序列化就是在保存数据时,保存数据的值和数据类型。
- 反序列化就是在恢复数据时,恢复数据的值和数据类型。
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializeable //这是一个标记接口,没有方法
- Externalizable//该接口有方法需要
节点流和处理流的使用细节:
- 读写顺序要一致。
- 要求实现序列化或反序列化对象,需要实现Serializable.
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性。
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员。
- 序列化对象时,要求里面属性的类型也需要现实序列化接口。
- 序列化具备可继承,也就是如果某类已经实现了序列化,则它的所有子类也默认实习序列化。
标准输入输出流:
类型 | 默认设备 | |
---|---|---|
System.in标准输入 | InputStream | 键盘 |
System.out标准输出 | PrintStream | 显示器 |
转换流-InputStreamReader 和 OutputStreamWriter
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
- OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所有建议将字节流转换成字符流。
- 可以在使用时指定编码格式(比如:utf-8、gb2312、ISO8859-1等)
String filePath = "路径";
//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk");
//BufferedReader br = new BufferedReader(isr);
BufferedReader br = new BufferedReader(new InputStream(new FileInputStream(filePath), "gbk"));//将第2行和第3行合并
String s = br.readLine();
System.out.println(" 读取内容 = "+ s );
br.close();//关闭外层流
String filePath = "路径";
String charSet = "GBK";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath),charSet);
osw.write("内容...")
osw.close();
打印流:PrintStream 和 PrintWriter 打印流只有输出流,没有输入流。
PrintStream out = System.out;
out.print("hello world");
out.close();
Properties类
-
专门用于读写配置文件的集合类
配置文件的格式:
键 = 值
键 = 值
-
注:键值对不需要有空格,值不需要引号–起来,默认类型是String
-
常见方法:
load :加载配置文件的键值对到Properities对象
list:将数据显示到指定设备
getProperty(key):根据键获取值
setProperty(key,value):设置键值对到Properties对象
store:将Properties中的键值对存储到配置文件中,保存信息到配置文件,如果含有中文,会存储为unicode
//使用Properties 类来读取mysql.properties文件
//1、创建Properties对象
Properties properties = new Properties();
//2、加载配置文件
properties.load(new FileReader("路径"));
//3、把K-V显示控制台
properties.list(System.out);
//根据Key 获取对应的值
//使用Properties类创建配置文件,修改配置文件内容
Properties properties = new Properties();
//创建:
//如果有该文件没有Key 就创建
//如果该文件有key,就修改
/*
Proerties 父类是 Hashtable ,底层就是Hashtable 核心方法
*/
properties.setProperty("charset","utf-8");
properties.setProperty("user","Tom");
properties.setProperty("pwd","11111");
//将K - V存储文件中即可
properties.store(new FileOutputStream("src\\mysql2.properties"),null);
System.out.println("yes");
网络的相关概念:
网络通信:
- 概念:两台设备之间通过网络实现数据传输。
- 网络通信:将数据通过网络从一台设备传输到另一台设备。
- java.net包下提供了一系列的类或接口供使用,完成网络通信。
网络:
-
概念:两台或多台设备通过一定物理设备连接起来构成了网络。
-
根据网络覆盖范围不同,对网络进行分类:
局域网:覆盖范围最小,仅仅覆盖一个脚上或一个机房。
城域网:覆盖范围较大,可以覆盖一个城市。
广域网:覆盖范围大,可以覆盖全国,甚至全球,万维网是广域网的代表。
ip地址:
- 概念:用于唯一标识网络中的每台计算机/主机
- 查看ip地址:ipconfig
- ip地址的表示形式:点分十进制 xx.xx.xx.xx
- 每一个十进制的范围:0~255
- ip地址的组成=网络地址+主机地址 eg:192.168.2.195
- iPv6是互联网工程任务组设计用于代替IPv4的下一代协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。
- 由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也可以解决了多种接入设备连入互联网的障碍。
- 对于IPV4 4个字节(32位)表示 1个字节的范围是 0 ~255
- A类 0.0.0.0 到 127.255.255.255
- B类 128.0.0.0到 191.255.255.255
- C类 192.0.0.0 到 239.255.255.255
- D类 240.0.0.0 到 247.255.255.255
- 特殊的 127.0.0.1 表示本机地址
- IPV6使用128位表示地址 16个字节 是IPV4的四倍
域名:
- www.baidu.com
- 好处:为了方便记忆,解决记ip的困难
- 概念:将ip地址映射成域名
端口号:
- 概念:用于 标识计算机上某个特定的网络程序。
- 表示形式:以整数形式,范围0~65535[2个字节表示端口 0 ~2^16 - 1]
- 0~1024已经被占用,比如 ssh 22 ftp 21 smtp 25 http 80
- 常见的网络程序端口号:
- tomcat:8080
- mysql:3306
- oracle:1521
- sqlServer:1433
网络通信协议:
TCP/IP 译为 传输控制协议/因特网互联协议,又名网络通信协议,这个协议是Internet最基本的协议,Internet国际互联网的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。
TCP 和 UDP
TCP协议:传输控制协议
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道。
- 传输前,采用"三次握手"方式,是可靠的。
- TCP协议进行通信的两个应用进程:客户端、服务端。
- 在连接中进行大数据量的传输。
- 传输完毕,需释放已建立的连接,效率低。
UDP协议:用户数据协议
- 将数据、源、目的封装成数据包,不需要建立连接。
- 每个数据报的大小限制在64K内,不适合传输大量数据。
- 因无需连接,故是不可靠的。
- 发送数据结束时无需释放资源(因为不是面向连接的),速度快。
InetAddress类
- 获取本机InetAddreaa对象getLocalHost
- 根据指定主机名/域名获取ip地址对象getByName
- 获取InetAddress对象的主机名getHostName
- 获取InetAddress对象的地址getHostAddress
Socket(套接字)
- 套接字开发网络应用程序被广泛采用,以至于成为事实上的标准。
- 通信的两端都要有Socket,是两台机器间通信的端点。
- 网络通信其实就是Socket间的通信方式。
- Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
- 一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端。
服务端(Server):
//思路
//1、在本机的9999端口监听,等待连接(要求其他服务没有监听999)
//这个ServerSocket 可以通过accept() , 返回多个Socket[多个客户端连接服务器的并发]
ServerSocket socketS = new ServerSocket(9999);
System.out.println("服务器等待连接...");
//2、当客户端连接999端口时,程序会阻塞,等待连接
Socket socket = socketS.accept();
System.out.println(socket.getClass());
//3、通过socket.getInputStream() 读取客户端写入到数据通道的数据显示
InputStream inputStream = socket.getInputStream();;
//4、IO读取
byte [] bufs = new byte[1024];
int readLen = 0;
while((readLen = inputStream.read(bufs)) != -1){
System.out.println(new String(bufs , 0 ,readLen));
}
inputStream.close();
socket.close();
socketS.close();
客户端(Client):
//思路:
//1、连接服务器(ip,端口)
//解读:连接本机的9999端口,如果连接成功,返回Socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端"+socket.getClass());
//2、连接上后,生成Socket,通过socket.getOutputStream();
//得到 和 socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3、通过输出流,写入数据到数据通道
outputStream.write("hello ".getBytes());
//4、关闭流对象和socket,必须关闭
outputStream.close();
socket.close();
设置写入结束标记:
socket.shutdownOutput(); 或者
writer.newLine()//换行符,注意需要使用readLine()
netstat指令
- netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
- netstat -an|more 可以分页显示
- 要求在dos控制台下执行 win+r
- Listening表示某端口在监听
- 如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息。
- 可以输入ctrl+c 退出指令
TCP网络通信不为人知的秘密:
- 当客户端连接到服务端后,实际上客户端也是通过一个端口号和服务端进行通讯的,这个端口是TCP/IP来分配的,是不确定的,随机的。
UDP网络通信编程
- 类DatagramSocket和DatagramPacket [数据包/数据报]实现了基于UDP 协议网络程序。
- UDP数据报通过数据套接字DatagramSocket发送和接受,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
- DatagramPacket 对象封装了UDP 数据报,在数据包中包含了发送端的IP地址和端口号以及接受端口号的IP和端口号。
- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。
基本流程:
- 核心的两个类/对象DatagramSocket与DatagramPacket
- 建立发送端,接收端(没有客户端和服务端概念)
- 发生数据前,建立数据包/报 DatagramPacket对象
- 调用DatagramSocket的发送,接收方法
- 关闭DatagramSocket
反射机制(Java Reflection):
- 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整的结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所有,形象的称之为:反射。
Java反射机制可以完成:
- 在运行时判断任意一个类型所属的类。
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的成员变量和方法。
- 生成动态代理
反射相关的主要类:
- java.lang.Class:代表一个类,Class对象表示某个类加载后堆中的对象
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法
// 使用反射机制解决问题
// (1)加载类,返回Class类型的对象
Class cls = Class.forName(classfullpath);
//(2)通过 cls 得到加载到类 com.hspedu.Cat的对象实例
Object o = cls.newInstance();
System.out.println( o.getClass());
System.out.println("=====================");
//(3) 通过 cls 得到你加载的类 com.hspedu.Cat 的methodName "h1"的方法对象
//即:在反射中 ,可以把方法视为对象
Method method1 =cls.getMethod(methodName);
//(4) 通过method1 调用方法:即通过方法对实现调用方法
method1.invoke(o);//传统方法 对象.方法() , 反射机制 方法.invoke(对象);
re.properties文件中的内容:
classfullpath=com.hspedu.Cat
methods=hi
反射的优点和缺点:
- 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
- 缺点:使用反射基本是解释执行,对执行速度有影响。
反射调用优化-关闭访问检查
- Method 和Field、Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁用访问安全检查的开关。
- 参数值为true表示 反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查。
Class类:
- Class也是类,因此也继承Object类
- Class类对象不是new出来的,而是系统创建的。
- 对于某个类的Class类对象,在内存中只有一份,因为类只加栽i一次。
- 每个类的实例都会记得自己是由哪个Class实例所生成。
- 通过Class可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码 ,变量名, 方法名,访问权限等等)
获取Class类对象:
-
前提:已知一个类的全类名,且该类在类途径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class cls1 = Class.forName(“java.lang.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类。
-
前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高。实例 Class cls2 = Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器现象。
-
前提:已知某个类的实例,调用该实例发getClass()方法获取Class对象,实例:Class clazz = 对象.getClass();//运行类型
-
其他方式
ClassLoader cl = 对象.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类 的全类名”);
-
基本数据类型(short 、byte、int、long、char、boolean、float、double)按如下方式得出Class类对象:Class cls = 基本数据类型.class
-
基本数据类型对应的包装类,可以通过.type 得到Class类对象。
Class cls = 包装类.TYPE
如下类型有Class对象:
- 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- interface:接口
- 数组
- enum:枚举
- annotation:注解
- 基本数据类型
- void
类加载:
反射机制是Java实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强。
- 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性。
类加载时机:
- 当创建对象时(new) //静态加载
- 当子类被加载时,父类也加载 //静态加载
- 调用类中的静态成员时 //静态加载
- 通过反射 //动态加载
类加载过程:
(Java源码)—javac—>(字节码文件)**—java运行—>**加载------>连接(验证–>准备–>解析)----->初始化
类加载:加载------>连接(验证–>准备–>解析)----->初始化
-
加载阶段:
JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包、甚至网路)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
-
连接 :
-
验证
- 目的是为了确保Class文件的字节流中包含的信息符号当前虚拟机的要求,并且不会危害虚拟机自身的要求。
- 包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和字符引用验证
- 可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
-
准备
- JVM会在该阶段对静态变量分配内存并默认初始化(对应数据类型的默认初始值,如 0 、0L、null、false等)
public int n1 = 10 ; public static int n2 = 20; public static final int n3 = 30; //1、n1是实例属性,不是静态变量,因此在准备阶段,是不会分配内存。 //n2 是静态变量,分配内存n2是默认初始化为0 ,而不是20 //n3是static final 是常量,他和静态变量不一样,因为一旦赋值就不变 n3 = 30
-
解析
- 虚拟机将常量池内的符号引用替换为直接引用的过程。
-
-
初始化:
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程
- ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
- 虚拟机会保证一个类()方法在多线程环境中被正确地加锁、同步、如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
通过反射创建对象:
-
方式一:调用类中的public修饰的无参构造器。
-
方式二:调用类中的指定构造器
-
Class类相关方法
newInstance:调用类中的无参构造器,获取对应类的对象。getConstructor(Class…clazz):根据参数列表,获取对应的public构造器对象。
getDecalaredConstructor(Class…clazz):根据参数列表,获取对应所有的构造器对象
-
Constructor类相关方法
setAccessible:暴破(使用反射可以访问private构造器)
newInstance(Object…obj):调用构造器
通过反射访问类中的成员:
访问属性:
-
根据属性名获取Field对象.
Field f = clazz对象.getDeclaredField(属性名)
-
暴破: f.setAccessible(true);//f 是Field 暴破指可以访问私有类型
-
访问:
f.set(o,值);
syso(f.get(o));
-
如果是静态属性,则set和get中的参数o,可以写成null。
访问方法:
- 根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaredMethod(方法名,XX.class);//本类所有方法
- 获取对象:Object o = clazz.newInstance();
- 暴破:m.setAccessible(true);
- 访问:Object returnValue = m.invoke(o,实参列表);
- 注:如果是静态方法,则invoke的参数o,可以写成null!
- 在反射中,如果方法有返回值,统一返回Object,但是他运行类型和方法定义的返回类型一致。