1,特殊文件
普通文件:txt
属性文件:properties
XML文件:xml
使用特殊文件:存储多个用户的用户名,密码,家乡,性别
存储有关系的数据,作为系统的配置文件
作为信息进行传输
这些特殊文件,我问主要学什么:
1,了解它们的特点、作用
2,学习使用程序读取它们里面的数据
3,学习使用程序把数据存储到这些文件里
(1)Properties
是一个Map集合(键值对集合),但是我们一般不会当集合使用
核心作用: Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容
使用Properties读取属性文件里的键值对数据
构造器 | 说明 |
public Properties() | 用于构建Properties集合对象(空容器) |
常用方法 | 说明 |
public void load(InputStream is) | 通过字节输入流,读取属性文件里的键值对数据 |
public void load(Reader reader) | 通过字符输入流,读取属性文件里的键值对数据 |
public String getProperty (String key) | 根据键获取值(其实就是get方法的效果) |
public Set<String> stringPropertyNames() | 获取全部键的集合(其实就是ketSet方法的效果) |
使用Properties把键值对数据写出到属性文件里去
构造器 | 说明 |
public Properties() | 用于构建Properties集合对象(空容器) |
常用方法 | 说明 |
public Object setProperty(String key,String value) | 保存键值对数据到Properties对象中去 |
public void store(OutputStream os, String comments) | 把键值对数据,通过字节输出流写出到属性文件里去 |
public void store(Writer w,String comments) | 把键值对数据,通过字符输出流写出到属性文件里去 |
//修改李方的数据 从35到18
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Properties;
public class TJ {
public static void main(String[] args) throws Exception {
Properties properties=new Properties();
properties.load(new FileReader("C:\\Users\\Lenovo\\IdeaProjects\\two\\two2\\src\\users.txt"));
if (properties.containsKey("李方")){
properties.setProperty("李方","18");
}
properties.store(new FileWriter("C:\\Users\\Lenovo\\IdeaProjects\\two\\two2\\src\\users.txt"),"success");
}
}
(2)XML文件
XML:(全称EXtensible Markup Language,可扩展标记语言)
本质是一种数据的格式,可以用来存储复杂的数据结构,和数据关系
1)XML的特点
1,XML中的“<标签名>”称为一个标签或一个元素,一般是成对出现的
2,XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套
3,XML中只能有一个根标签
4,XML中的标签可以有属性
5,如果一个文件中放置的是XML格式的数据,这个文件就是XML文件,后缀一般要写成.xml
2)XML文件的创建
就是创建一个XML类型的文件,要求文件的后缀必须使用xml,如hello_world.xml
3)XML的语法规则
1,XML文件的后缀名为: xml,文档声明必须是第一行
<?xml version="1.0" encoding="UTF-8" ?>
version:XML默认的版本号码、该属性是必须存在的
encoding:本XML文件的编码
2,XML中可以定义注释信息:<!--注释内容-->
3,XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代
< | < 小于 |
> | > 大于 |
& | & 和号 |
' | ' 单引号 |
&qout; | " 引号 |
4,XML中可以写一个叫CDATA的数据区:<![CDATA[ ...内容...],里面的内容可以随便写
<?xml version="1.0" encoding="UTF-8" ?>
<!--注释:以上抬头声明必须放在第一行,必须有-->
<!--根标签只能有一个-->
<Users>
<user id="1">
<name>张无忌</name>
<sex>男</sex>
<password>123456</password>
<date> 3 < 2 &&5 > 4</date>
<data1>
[<![CDATA[
3<2 &&5>4
]]>
</data1>
</user>
<user id="2">
<name>敏敏</name>
<sex>女</sex>
<password>456987</password>
</user>
</Users>
4)XML的作用和应用场景
本质是一种数据格式,可以存储复杂的数据结构,和数据关系
应用场景:经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输
5)解析XML文件
使用程序读取XML文件中的数据
注意:程序员并不需要自己写原始的IO流代码来解析XML,难度较大!也相当繁琐!
其实,有很多开源的,好用的,解析XML的框架,最知名的是:Dom4j(第三方研发的)
Dom4j解析XML-得到Document对象
SAXReader: Dom4j提供的解析器,可以认为是代表整个Dom4j框架
构造器/方法 | 说明 |
public SAXReader() | 构建Dom4J的解析器对象 |
public Document read(String url) | 把XML文件读成Document对象 |
public Document read(InputStream is) | 通过字节输入流读取XML文件 |
Document
方法名 | 说明 |
Element getRootElement() | 获得根元素对象 |
Element提供的方法
方法名 | 说明 |
public String getName( ) | 得到元素名字 |
public List<Element> elements() | 得到当前元素下所有子元素 |
public List<Element> elements(String name) | 得到当前元素下指定名字的子元素返回集合 |
public Element element(String name) | 得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个 |
public String attributeValue(String name) | 通过属性名直接得到属性值 |
public String elementText(子元素名) | 得到指定名称的子元素的文本 |
public String getText() | 得到文本 |
public String getTextTrim() | 取出文本去除前后空格 |
如何使用程序把数据写出到XML文件中去?
不建议用dom4j做
推荐直接把程序里的数据拼接成XML格式,然后用IO流写出去
import java.io.BufferedWriter;
import java.io.FileWriter;
public class XML {
public static void main(String[] args) {
StringBuilder sb=new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n");
sb.append("<Users>\r\n");
sb.append("<name>").append("张无忌").append("</name>\r\n");
sb.append("<password>").append("123456").append("<password>\r\n");
sb.append("<Users>");
try (BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\" +
"Users\\Lenovo\\IdeaProjects\\two\\two2\\src\\helloworld.xml"))){
bw.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();;
}
}
}
6)约束XML文件的书写
就是限制XML文件只能按照某种格式进行书写
约束文档
专门用来限制xml书写格式的文档,比如:限制标签、属性应该怎么写
【1】DTD文档
可以约束XML文件的编写
不能约束具体的数据类型
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 书架 SYSTEM "data.dtd">
【2】Schema文档
可以约束XML文件的编写、和数据类型
2,日志技术
把程序运行的信息,记录到文件中,方便程序员定位bug、并了解程序的执行情况等
程序中的日志,通常就是一个文件,里面记录的是程序运行过程中的各种信息
输出语句的弊端:
1,日志会在控制台
2,不能更方便的将日志记录到其他的位置(文件,数据库)
3,想取消日志,需要修改源代码才可以完成
日志技术:
可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)
可以随时以开关的形式控制日志的启停,无需侵入到源代码中去进行修改
(1)日志技术的体系结构
日志框架:牛人或者第三方公司已经做好的实现代码,后来者直接可以拿去使用
1,JUL(Java.util.logging)
2,Log4j
3,Logback
4,其他实现
日志接口:设计日志框架的一套标准,日志框架需要实现这些接口
1,Commons logging (JCL)
2,Simple Logging Facade for Java (SLF4J)
注意1∶因为对Commons Logging接口不满意,有人就搞了SLF4J;因为对Log4j的性能不满意,有人就搞了Logback
注意2: Logback是基于slf4j的日志规范实现的框架
1)Logback日志框架
模块:1,logback-core 基础模块,是其他两个模块依赖的基础(必须有)
2,logback-classic 完整实现了slf4j API的模块(必须有)
3,logback-access 与Tomcat和Jetty 等Servlet容器集成,以提供HTTP访问日志的功能(可选,以后再接触)
想使用Logback日志框架,至少需要以下3个模块:
1,slf4j-api:日志接口
2,logback-core
3,logback-classic
public static final Logger LOGGER = LoggerFactory.getLogger(“类名");
2)核心配置文件logback.xml
对Logback日志框架进行控制的
日志的输出位置、输出格式的设置
通常可以设置2个输出日志的位置:一个是控制台、一个是系统文件中
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
开启日志(ALL),取消日志(OFF)
<root level=ALL">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE”/>
< /root>
(2)日志级别
日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级依次升高)
日志级别 | 说明 |
trace | 追踪,指明程序运行轨迹 |
debug | 调试,实际应用中一般将其作为最低级别,而trace则很少使用 |
info | 输出重要的运行信息,数据连接、网络连接、IO操作等等,使用较多 |
warn | 警告信息,可能会发生问题,使用较多 |
error | 错误信息,使用较多 |
学习日志级别的原因:
只有日志的级别是大于或等于核心配置文件配置的日志级别,才会被记录,否则不记录
3,多线程
线程:线程(Thread)是一个程序内部的一条执行流程
程序中如果只有一条执行流程,那这个程序就是单线程的程序
多线程的定义:
多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)
好处:再例如:消息通信、淘宝、京东系统都离不开多线程技术
如何在程序中创建出多条线程:
Java是通过java.lang.Thread类的对象来代表线程的
线程的创建
1)继承Thread类
1,定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
2,创建MyThread类的对象
3,调用线程对象的start()方法启动线程(启动后还是执行run方法的)
public class ThreadTest1 {
public static void main(String[] args) {
//创建对象
Thread t=new MyThread();
t.start();//main线程 t线程
for (int i = 1; i <=5 ; i++) {
System.out.println("主线程main线程输出:"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
//描述线程的执行任务
for (int i = 1; i <=5 ; i++) {
System.out.println("MyThread线程输出:"+i);
}
}
}
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展
多线程的注意事项:
1、启动线程必须是调用start方法,不是调用run方法
直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
只有调用start方法才是启动一个新的线程执行
2、不要把主线程任务放在启动子线程之前
这样主线程一直是先跑完的,相当于是一个单线程的效果了
2)实现Runnable接口
1,定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
2,创建MyRunnable任务对象
3,把MyRunnable任务对象交给Thread处理
Thread类提供的构造器 | 说明 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
4,调用线程对象的start()方法启动线程
public class ThreadTest2 {
public static void main(String[] args) {
//创建任务对象
Runnable target=new MyRunnable();
//把任务对象交给线程对象
new Thread(target).start();
for (int i = 1; i <=5 ; i++) {
System.out.println("主线程main输出===》"+i);
}
}
}
class MyRunnable implements Runnable{
//重写Runnable的run方法
@Override
public void run() {
//线程执行的任务
for (int i = 1; i <=5 ; i++) {
System.out.println("子线程输出===》"+i);
}
}
}
优点: 任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强
缺点:需要多一个Runnable对象
线程创建方式二的匿名内部类写法
1,可以创建Runnable的匿名内部类对象
2,再交给Thread线程对象
3,再调用线程对象的start()启动线程
Runnable target=new Runnable() {
@Override
public void run() {
for (int i = 1; i <=5 ; i++) {
System.out.println("子线程1输出:"+i);
}
}
};
new Thread(target).start();
for (int i = 1; i <=5 ; i++) {
System.out.println("主线程main输出:"+i);
}
//简化后1
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=5 ; i++) {
System.out.println("子线程2输出:"+i);
}
}
}).start();
for (int i = 1; i <=5 ; i++) {
System.out.println("主线程main输出:"+i);
}
//简化后2
new Thread(()-> {
for (int i = 1; i <=5 ; i++) {
System.out.println("子线程3输出:"+i);
}
}).start();
for (int i = 1; i <=5 ; i++) {
System.out.println("主线程main输出:"+i);
}
3)利用Callable接口,FutureTask类实现
前两种方法的问题:
假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果
解决方法:
JDK 5.0提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式)
1,创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据
把Callable类型的对象封装成FutureTask(线程任务对象)
2,把线程任务对象交给Thread对象
3,调用Thread对象的start方法启动线程
4,线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果
public class ThreadTest3 {
public static void main(String[] args) throws Exception {
Callable<String> call=new MyCallable(100);
FutureTask<String> f1 =new FutureTask<>(call);
new Thread(f1).start();
String rs=f1.get();
System.out.println(rs);
}
}
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum=0;
for (int i = 1; i <=n ; i++) {
sum+=i;
}
return "线程求和是:"+sum;
}
}
FutureTask的API
FutureTask提供的构造器 | 说明 |
public FutureTask<>(Callable call) | 把callable对象封装成FutureTask对象 |
FutureTask提供的方法 | 说明 |
public v get() throws Exception | 获取线程执行call方法返回的结果 |
优点:
线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果
缺点:
编码复杂一点