配置文件
介绍
配置文件
- 在企业开发过程中,我们习惯把一些需要灵活配置的数据放在一些文本文件中,而不是在Java代码写死
- 我们把这种存放程序配置信息的文件,统称为配置文件
Properties
- 是一个Map集合(键值对集合),但是我们一般不会当集合使用。
- 核心作用:Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容。
使用Properties读取属性文件里的键值对数据
使用Properties把键值对数据写出到属性文件里去
/*
properties配置文件
特点:
1、都只能是键值对
2、键不能重复
3、文件后缀一般是.properties结尾的
Properties
这是一个Map集合(键值对集合),但是我们一般不会当集合使用
主要用来代表属性文件,通过Properties可以读写属性文件里的内容
读取
public Properties() 创建Properties集合对象
public void load(InputStream is/Reader reader) 通过输入流读取文件中的键值对数据
public String getProperty(String key) 根据键获取值(其实就是get方法的效果)
public Set<String> stringPropertyNames() 获取全部键的集合(其实就是ketSet方法的效果)
写出
public Properties() 创建Properties集合对象
public Object setProperty(String key, String value) 保存键值对数据到Properties对象中去。
public void store(OutputStream os/Writer writer, String comments) 把键值对数据,通过输出流写出到属性文件里去
*/
public class Demo1 {
public static void main(String[] args) throws IOException {
//1. 创建一个配置文件对象
Properties prop = new Properties();
prop.load(new FileReader("day10-code/user.properties"));
//2. 读取
String admin = prop.getProperty("admin");
System.out.println(admin);
Set<String> keys = prop.stringPropertyNames();
for (String key : keys) {
String value = prop.getProperty(key);
System.out.println(key + "--" + value);
}
//3. 写出
Properties prop2 = new Properties();
prop2.setProperty("SSS", "888");
prop2.setProperty("AAA", "999");
prop2.store(new FileWriter("day10-code/user2.properties"),"abc");
}
}
1、Properties的作用?具体如何使用?
可以加载属性文件中的数据到Properties对象中来
void load(Reader reader)
public String getProperty(String key) 根据键获取值
可以存储Properties属性集的键值对数据到属性文件中去
void store(Writer writer, String comments) 设置键值
public Object setProperty(String key, String value)
XML
认识XML
XML
- 全称Extensible Markup Language, 可扩展标记语言
- 本质是一种数据的格式,可以用来存储复杂的数据结构,和数据关系。
XML的特点
- XML中的“<标签名>” 称为一个标签或一个元素,一般是成对出现的。
- XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套。
- XML中只能有一个根标签。
- XML中的标签可以有属性。
- 如果一个文件中放置的是XML格式的数据,这个文件就是XML文件,后缀一般要写成.xml。
XML的语法规则
- XML文件的后缀名为:xml,文档声明必须是第一行
- XML中可以定义注释信息:<!–- 注释内容 -->,快捷键是Ctrl+shift+/
- XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代。
- XML中可以写一个叫CDATA的数据区: <![CDATA[ …内容… ]]>,里面的内容可以随便写。直接输入CD,就会有提示
<?xml version="1.0" encoding="UTF-8" ?>
<!--这是注释内容-->
<users>
<user id="1">
<name>张无忌</name>
<password>minmin</password>
<address>光明顶</address>
<gender>男</gender>
</user>
<user id="2">
<name>敏敏</name>
<password><![CDATA[
< > &
]]></password>
<address>光明顶</address>
<gender>女</gender>
</user>
</users>
XML的作用和应用场景
- 本质是一种数据格式,可以存储复杂的数据结构,和数据关系。
- 应用场景:经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输。
1、XML是什么? 有啥作用?
XML的是一种可扩展的标记语言。
作用:1)作为软件的配置文件 2)用于进行存储数据和传输数据
2、XML的组成格式要求是什么样的?
文件后缀一般是是xml,文档声明必须是第一行
必须存在一个根标签,有且只能有一个
XML文件中可以定义注释信息:<!–- 注释内容 -->
标签必须成对出现,有开始,有结束标签: <name></name>
必须能够正确的嵌套
读取XML
解析XML文件
- 使用程序读取XML文件中的数据
DOM4J解析XML文件的思想:文档对象模型
Dom4j解析XML-得到Document对象
SAXReader:Dom4j提供的解析器,可以认为是代表整个Dom4j框架
构造器/方法 | 说明 |
public SAXReader() | 构建Dom4J的解析器对象 |
public Document read(String path) | 把XML文件读成Document对象 |
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() | 得到文本 |
案例:
/*
需求: 解析b-2.xml,将内容封装到一个List<User对象的集合>
XML解析
DOM解析思想
DOM (document Object Model)文档对象模型
将文档的各个组成部分看做是对应的对象
首先会将xml文件全部加载到内存,在内存中形成一个树状结构,在获取对应的值
DOM解析思想就是一层一层的进入,一层一层的解析
对象分类
Document:整个xml文档对象
Element: 标签对象
Attribute:属性对象
Text标签体:文本对象
DOM4J解析XML准备工作
1、将dom4j的jar包加入到项目中
2、创建DOM4]解析器对象SAXReader,获取Document文档对象,获取根标签对象
3、从根标签对象开始往下层层解析
详细API
SAXReader:Dom4j提供的解析器,可以认为是代表整个Dom4j框架
public SAXReader() 构建Dom4J的解析器对象
public Document read(String path) 把XML文件读成Document对象
Element getRootElement() 获得根元素对象
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 class Demo2 {
public static void main(String[] args) throws Exception {
ArrayList<User> users = new ArrayList<>();
// 1、将dom4j的jar包加入到项目中
// 2、创建DOM4]解析器对象SAXReader,获取Document文档对象,获取根标签对象
SAXReader saxReader = new SAXReader();
//读取xml文件
Document document = saxReader.read(new FileReader("day10-code/b-2.xml"));
// 3、从根标签对象开始往下层层解析
Element rootElement = document.getRootElement();
//获取根标签的名字
String rootName = rootElement.getName();
System.out.println("name=" + rootName);
//获取根标签下的所有子标签
List<Element> elements = rootElement.elements();
System.out.println(elements.size());
//遍历标签的集合
for (Element element : elements) {
String name = element.getName();
String id = element.attributeValue("id");
System.out.println("子标签名字:" + name + id);
List<Element> elements1 = element.elements();
System.out.println(elements1.size());
String userName = element.element("name").getText();
System.out.println("name" + userName);
String password = element.element("password").getText();
System.out.println("password" + password);
String address = element.element("address").getText();
System.out.println("address" + address);
String gender = element.element("gender").getText();
System.out.println("gender" + gender);
User user = new User(userName, password, address, gender);
users.add(user);
}
for (User user : users) {
System.out.println(user);
}
}
}
class User{
private String name;
private String password;
private String address;
private String gender;
public User() {
}
public User(String name, String password, String address, String gender) {
this.name = name;
this.password = password;
this.address = address;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String toString() {
return "User{name = " + name + ", password = " + password + ", address = " + address + ", gender = " + gender + "}";
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<users>
<user id="1">
<name>张无忌</name>
<password>minmin</password>
<address>光明顶</address>
<gender>男</gender>
</user>
<user id="2">
<name>敏敏</name>
<password>wuji</password>
<address>光明顶</address>
<gender>女</gender>
</user>
</users>
XML约束
什么是约束XML文件的书写?
- 就是限制XML文件只能按照某种格式进行书写。
约束文档
- 专门用来限制xml书写格式的文档,比如:限制标签、属性应该怎么写。
约束文档的分类
- DTD文档
- Schema文档
XML文档约束-DTD的使用(了解)
需求:利用DTD约束文档,约束一个XML文件的编写。
①:编写DTD约束文档,后缀必须是.dtd
②:在需要编写的XML文件中导入该DTD约束文档
③:然后XML文件,就必须按照DTD约束文档指定的格式进行编写,否则报错!
XML文档约束-schema的使用(了解)
需求:利用schema文档约束,约束一个XML文件的编写。
①:编写schema约束文档,后缀必须是.xsd,具体的形式到代码中观看。
②:在需要编写的XML文件中导入该schema约束文档
③:按照约束内容编写XML文件的标签。
/*
案例需求: 将如下数据写入到b-3.xml中
<?xml version="1.0"encoding="UTF-8"?>
<book>
<name>Java从入门到放弃</name>
<author>黑马-小明老师</author>
<price>89.5</price>
</book>
如何从程序中写数据到xml文件中
不建议用DOM4J将数据写入到xml文件中
推荐在java代码中,将数拼接成xml格式,用IO流写出去
*/
public class Demo3 {
public static void main(String[] args) throws Exception {
//1.拼接xml内容的字符串
//使用可变字符串对象去拼接
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\"encoding=\"UTF-8\"?>\r\n");
sb.append("<book>\r\n");
sb.append("<name>Java从入门到放弃</name>\r\n");
sb.append("<author>黑马-小明老师</author>\r\n");
sb.append("<price>89.5</price>\r\n");
sb.append("</book>\r\n");
//拼接完成后返回字符串
String xmlStr = sb.toString();
//2.IO流写到文件
try (
FileWriter writer = new FileWriter("day10-code/b-3.xml")
){
//使用Writer, FileWriter
//把拼接好的字符串写到文件中
writer.write(xmlStr);
//关闭流
// writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
日志文件
概述
日志技术
- 可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)。
- 可以随时以开关的形式控制日志的启停,无需侵入到源代码中去进行修改。
日志技术的体系结构
- 日志接口:设计日志框架的一套标准,日志框架需要实现这些接口。
- 日志框架:牛人或者第三方公司已经做好的实现代码,后来者直接可以拿去使用。
- 注意1:因为对Commons Logging接口不满意,有人就搞了SLF4J;因为对Log4j的性能不满意,有人就搞了Logback。
- 注意2:Logback是基于slf4j的日志规范实现的框架。
1、什么是日志?
用来记录程序运行过程中的信息,并可以进行永久存储。
2、常见的日志实现框架有哪些?
Log4J、Logback(我们重点学习的,其他的都大同小异)
Logback是基于slf4j日志接口实现的日志框架
3、使用Logback至少需要使用哪几个模块?
slf4j-api:日志接口
logback-core:基础模块
logback-classic:功能模块,它完整实现了slf4j API
Logback快速入门
需求
实现步骤
①:导入Logback框架到项目中去。
②:将Logback框架的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。
③:创建Logback框架提供的Logger对象,然后用Logger对象调用其提供的方法就可以记录系统的日志信息 。
/*
需求
使用Logback日志框架,纪录系统的运行信息。
实现步骤
1. 导入Logback框架到项目中去。
2. 将Logback框架的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。
4. 创建Logback框架提供的Logger对象,然后用Logger对象调用其提供的方法就可以记录系统的日志信息。
public static final Logger LOGGER = LoggerFactory.getLogger("当前类类名");
5. 打印日志
LOGGER.info("日志信息")
LOGGER.debug("日志信息")
LOGGER.error("日志信息")
*/
public class Demo1 {
public static final Logger LOGGER = LoggerFactory.getLogger("Demo1");
public static void main(String[] args) {
LOGGER.info("你喜欢跳舞吗?");
div(5,2);
div(5,0);
}
public static int div(int a, int b) {
int c = 0;
try {
LOGGER.debug("方法执行了,a={},b={}", a,b);
c = a / b;
} catch (Exception e) {
LOGGER.error("{}/{}出错了", a, b);
e.printStackTrace();
}
return c;
}
}
核心配置文件logback.xml
对Logback日志框架进行控制的。
日志的输出位置、输出格式的设置
通常可以设置2个输出日志的位置:一个是控制台、一个是系统文件中
Logback设置日志级别
什么是日志级别?
日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级依次升高):
日志级别 | 说明 |
trace | 追踪,指明程序运行轨迹 |
debug | 调试,实际应用中一般将其作为最低级别,而 trace 则很少使用 |
info | 输出重要的运行信息,数据连接、网络连接、IO操作等等,使用较多 |
warn | 警告信息,可能会发生问题,使用较多 |
error | 错误信息, 使用较多 |
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/log/itheima-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/log/itheima-data-%i-%d{yyyy-MM-dd}-.log.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
1、控制日志的输出情况:如,开启日志,取消日志
-->
<root level="ALL">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
为什么要学习日志级别?
只有日志的级别是大于或等于核心配置文件配置的日志级别,才会被记录,否则不记录。
1、设置日志输出级别的作用是什么?
用于控制系统中哪些日志级别是可以输出的
2、Logback的日志级别是什么样的?
ALL 和 OFF分别是打开全部日志和关闭全部日志
级别程度依次是:TRACE< DEBUG< INFO<WARN<ERROR
默认级别是debug(忽略大小写),只输出当前级别及高于该级别的日志
多线程
什么是线程?
- 线程:简单的说,就是计算机在做一件事
- 单线程:在计算机中同一时间只能做一件事
- 多线程:在计算机中同一时间可以做多件事
多线程在程序中的应用
线程的创建方式
继承Thread类
多线程的创建方式一:继承Thread类
- 定义一个子类继承线程类java.lang.Thread,重写run()方法
- 创建子类的对象
- 调用子类对象的start()方法启动线程(底层会自动去执行run方法
方式一优缺点
- 优点:编码简单
- 缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
多线程的注意事项
1、启动线程必须是调用start方法,不是调用run方法。
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
- 只有调用start方法才是启动一个新的线程执行。
2、不要把主线程任务放在启动子线程之前。
- 这样主线程一直是先跑完的,相当于是一个单线程的效果了。
/*
多线程
让程序同时做多件事
多线程的创建方式一:继承Thread类
1. 定义一个子类继承线程类java.lang.Thread,重写run()方法
2. 创建子类的对象
3. 调用子类对象的start()方法启动线程(底层会自动去执行run方法)
优缺点
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
注意事项
1、启动线程必须是调用start方法,不是调用run方法。
2、直接调用run方法会当成普通方法执行,只有调用start方法才是启动一个新的线程执行。
3、不要将主线任务放在start方法之前,这样主线程一直是先跑完的,相当于是一个单线程的效果了。
扩展
对于单核cpu来讲, 多线程是一种假象
*/
public class Demo1 {
public static final Logger LOGGER = LoggerFactory.getLogger("Demo1");
public static void main(String[] args) {
//需求:创建两个线程,分别用于打印10个A和10个B,最后观察下输出顺序
AThread aThread = new AThread();
BThread bThread = new BThread();
aThread.start();
bThread.start();
for (int i = 0; i <20; i++) {
LOGGER.debug("main-" + i);
}
}
}
class AThread extends Thread {
public static final Logger LOGGER = LoggerFactory.getLogger("AThread");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.info("A-" + i);
}
}
}
class BThread extends Thread {
public static final Logger LOGGER = LoggerFactory.getLogger("BThread");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.info("B-" + i);
}
}
}
1、什么是多线程?
同时做多件事
2、线程创建方式一的具体步骤是?
- 继承Thread类,重写run方法
- 创建线程对象
- 调用start()方法启动
3、线程的第一种创建方式有啥优缺点?
- 优点:编码简单
- 缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展
实现Runnable接口
多线程的创建方式二:实现Runnable接口
Thread类提供的构造器 | 说明 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
方式二的优缺点
- 优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
- 缺点:需要多一个Runnable对象。
/*
多线程的创建方式二:实现Runnable接口
1. 定义一个线程任务类实现Runnable接口,重写run()方法
2. 创建任务类对象
3. 把任务类对象交给Thread处理
public Thread(Runnable target)
4. 调用线程对象的start()方法启动线程
优缺点
优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
缺点:需要多一个Runnable对象。
*/
public class Demo1 {
public static void main(String[] args) {
//需求:创建两个线程,分别用于打印10个A和10个B,最后观察下输出顺序
AThread aThread = new AThread();
BThread bThread = new BThread();
Thread t1 = new Thread(aThread);
Thread t2 = new Thread(bThread);
t1.setName("t1-a");
t2.setName("t2-b");
t1.start();
t2.start();
}
}
class AThread implements Runnable{
public static final Logger LOGGER = LoggerFactory.getLogger("AThread");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.debug("A-" + i);
}
}
}
class BThread implements Runnable{
public static final Logger LOGGER = LoggerFactory.getLogger("BThread");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.debug("B-" + i);
}
}
}
1、线程创建方式二是如何创建线程的?
定义一个线程任务类实现Runnable接口,重写run()方法
创建任务类对象
把任务对象交给Thread线程对象处理
调用线程对象的start()方法启动线程
2、方式二的优缺点是啥?
优点:线程任务类只是实现了Runnale接口,可以继续继承和实现
缺点:如果线程有执行结果是不能直接返回的
实现Callable接口
多线程的第三种创建方式:利用Callable接口、FutureTask类来实现。
- 定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。
- 把Callable类型的对象封装成FutureTask(线程任务对象)。
FutureTask的API
FutureTask提供的构造器 | 说明 |
public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
FutureTask提供的方法 | 说明 |
public V get() throws Exception | 获取线程执行call方法返回的结果。 |
线程创建方式三的优缺点
- 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
- 缺点:编码复杂一点。
/*
多线程的创建方式三:实现Callable接口
1. 创建任务对象
定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据
把Callable类型的对象封装成FutureTask(线程任务对象)
public FutureTask<>(Callable call) 把Callable对象封装成FutureTask对象
public V get() throws Exception 获取线程执行call方法返回的结果
2. 把线程任务对象交给Thread对象。
3. 调用Thread对象的start方法启动线程。
4. 线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果
优缺点
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果
缺点:编码复杂一点。
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//需求:启动两个子线程,分别计算100之内的奇数的和和偶数的和,然后在主线程中再做个汇总,得到总和
//创建callable的对象
OddThread oddThread = new OddThread();
EvenThread evenThread = new EvenThread();
//创建FutureTask对象
FutureTask<Integer> oddFutureTask = new FutureTask<>(oddThread);
FutureTask<Integer> evenFutureTask = new FutureTask<>(evenThread);
//创建线程对象
Thread oddT = new Thread(oddFutureTask, "奇数线程");
Thread evenT = new Thread(evenFutureTask, "偶数线程");
//调用线程的对象
oddT.start();
evenT.start();
//获取线程的结果
Integer oddSum = oddFutureTask.get();
Integer evenSum = evenFutureTask.get();
int sum = oddSum + evenSum;
System.out.println(sum);
}
}
//计算100以内的奇数和,并返回
class OddThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i+=2) {
sum += i;
}
return sum;
}
}
//计算100以内的偶数和,并返回
class EvenThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i+=2) {
sum += i;
}
return sum;
}
}
请对对比说一下三种线程的创建方式,和不同点?
方式 | 优点 | 缺点 |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他的类,不能返回线程执行的结果 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。 | 编程相对复杂,不能返回线程执行的结果 |
实现Callable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果 | 编程相对复杂 |
Thread类的方法
Thread线程操作相关的方法
Thread线程获取线程、设置线程、拿到当前线程对象
Thread线程休眠、线程join
Thread其他方法说明