一、配置文件
1.1 properties配置文件
properties配置文件 特点: 1、都只能是键值对 2、键不能重复 3、文件后缀一般是.properties结尾的 Properties 这是一个Map集合(键值对集合),但是我们一般不会当集合使用 主要用来代表属性文件,通过Properties可以读写属性文件里的内容
构造器 | 说明 |
---|---|
public Properties() | 用于构建Properties集合对象(空容器) |
使用Properties读取属性文件里的键值对数据
读取:磁盘---->内存
常用方法 | 说明 |
---|---|
public void load(InputStream is) | 通过字节输入流,读取属性文件里的键值对数据 |
public void load(Reader reader) | 通过字符输入流,读取属性文件里的键值对数据 |
public String getProperty(String key) | 根据键获取值(其实就是get方法的效果) |
public Set<String> stringPropertyNames() | 获取全部键的集合(其实就是ketSet方法的效果) |
public static void main(String[] args) throws IOException {
//1. 创建一个配置文件对象
Properties properties = new Properties();
//2. 读取
properties.load(new FileInputStream("day10-code\\xml\\user.properties"));
// String value = properties.getProperty("admin");
// System.out.println("admin = " + value);
// String lisi = properties.getProperty("lisi","000000");
// System.out.println("lisi = " + lisi);
Set<String> keySet = properties.stringPropertyNames();
for (String key : keySet) {
String value = properties.getProperty(key);
System.out.println(key + " = " + value);
}
}
使用Properties把键值对数据写出到属性文件里去
写入:内存---->磁盘
常用方法 | 说明 |
---|---|
public Object setProperty(String key, String value) | 保存键值对数据到Properties对象中去。 |
public void store(OutputStream os, String comments) | 把键值对数据,通过字节输出流写出到属性文件里去 |
public void store(Writer w, String comments) | 把键值对数据,通过字符输出流写出到属性文件里去 |
public static void main(String[] args) throws IOException {
// 写出
Properties pro2 = new Properties();
pro2.setProperty("zhu", "猪小贱");
pro2.setProperty("lin","哼哼猪");
pro2.setProperty("pang","羊习习");
pro2.store(new FileOutputStream("day10-code\\xml\\user2.properties"),null);
}
总结
Properties的作用?具体如何使用?
1.可以加载属性文件中的数据到Properties对象中来
void load(Reader reader)
public String getProperty(String key) 根据键获取值
2.可以存储Properties属性集的键值对数据到属性文件中去
void store(Writer writer, String comments) 设置键值
public Object setProperty(String key, String value)
1.2 XML配置文件
XML 全称 Extensible Markup Language, 可扩展标记语言 本质是一种数据的格式,可以用来存储复杂的数据结构,和数据关系。 特点 XML中的“<标签名>” 称为一个标签或一个元素,一般是成对出现的。 XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套。 XML中只能有一个根标签。 XML中的标签可以有属性。 如果一个文件中放置的是XML格式的数据,这个文件就是XML文件,后缀一般要写成.xml。 语法 XML文件的后缀名为:xml,文档声明必须是第一行 XML中可以定义注释信息:<!–- 注释内容 -->,快捷键是Ctrl+shift+/ XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代。 XML中可以写一个叫CDATA的数据区: <![CDATA[ …内容… ]]>,里面的内容可以随便写。 XML的作用和应用场景 作用:1)作为软件的配置文件 2)用于进行存储数据和传输数据 经常用来做为系统的配置文件 或者作为一种特殊的数据结构,在网络中进行传输。
1.2.1 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() | 得到文本 |
XML解析案例
需求:
利用Dom4J框架,将contacts.xml文件中的联系人数据,解析出来,封装成List集合,并遍历输出。
public class Demo2 {
public static void main(String[] args) throws FileNotFoundException, DocumentException {
List<User> userList = new ArrayList();
//2.创建DOM4]解析器对象SAXReader,获取Document文档对象,获取根标签对象
SAXReader saxReader = new SAXReader();
//读取XML文件,读取到内存
Document document = saxReader.read(new FileReader("day10-code/user.xml"));
//3.从根标签对象开始往下层层解析
//3.1 获取根标签的名字
Element rootElement = document.getRootElement();
String name = rootElement.getName();
System.out.println("根标签的名字:" + name);
//3.2 获取根标签下的所有子标签
List<Element> elements = rootElement.elements();
System.out.println("根标签下的所有子标签个数:" + elements.size());
//3.3 遍历标签的集合
for (Element element : elements) {
System.out.println("==========================");
String id = element.attributeValue("id");
String elementName = element.getName();
System.out.println("子标签的名字:" + elementName + "-" + id);
String userName = element.element("name").getText();
System.out.println("子标签下的name标签的文本:name=" + userName);
String userPassword = element.element("password").getText();
System.out.println("子标签下的password标签的文本:password=" + userPassword);
String userAddress = element.element("address").getText();
System.out.println("子标签下的address标签的文本:address=" + userAddress);
String userGender = element.element("gender").getText();
System.out.println("子标签下的gender标签的文本:gender=" + userGender);
//3.4 封装对象到一个List<User对象的集合>
userList.add(new User(userName, userGender, userAddress, userPassword));
}
System.out.println("==========================");
System.out.println(userList);
}
}
class User{
private String name;
private String gender;
private String address;
private String password;
public User() {
}
public User(String name, String gender, String address, String password) {
this.name = name;
this.gender = gender;
this.address = address;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
", password='" + password + '\'' +
'}';
}
}
二、日志
1、什么是日志?
用来记录程序运行过程中的信息,并可以进行永久存储。
2、常见的日志实现框架有哪些?
Log4J、Logback(我们重点学习的,其他的都大同小异)
Logback是基于slf4j日志接口实现的日志框架
3、使用Logback至少需要使用哪几个模块?
slf4j-api:日志接口
logback-core:基础模块
logback-classic:功能模块,它完整实现了slf4j API
2.1 Logback
需求 使用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("日志信息")//try-catch中给错误信息打日志
public class Demo1 {
public static final Logger LOGGER = LoggerFactory.getLogger("Demo1");
public static void main(String[] args) {
LOGGER.info("开始执行div方法");
div(2,2);
div(2,0);
}
public static int div(int a,int b){
LOGGER.debug("方法执行了,a="+ a + ",b="+b);
LOGGER.debug("方法执行了,a={},b={}",a,b);
LOGGER.warn("除数不能为0");
try {
int c = a/b;
return c;
}catch (Exception e){
LOGGER.error("方法执行出错了,出错信息是{}/{}",a,b);
e.printStackTrace();
throw new RuntimeException("出错了");
}
}
}
2.2 日志级别
1、设置日志输出级别的作用是什么?
用于控制系统中哪些日志级别是可以输出的
2、Logback的日志级别是什么样的?
ALL 和 OFF分别是打开全部日志和关闭全部日志
级别程度依次是:TRACE< DEBUG< INFO<WARN<ERROR
默认级别是debug(忽略大小写),只输出当前级别及高于该级别的日志
三、多线程
线程 简单的说,就是计算机在做一件事 单线程 在计算机中同一时间只能做一件事 多线程 在计算机中同一时间可以做多件事 它的主要好处有:1. 减少队列阻塞带来的影响 2. 提高CPU的利用率
3.1 多线程的创建方式一:继承Thread类
线程的创建方式一:继承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,最后观察下输出顺序
// 2. 创建子类的对象
AThread aThread = new AThread();
BThread bThread = new BThread();
// 3. 调用子类对象的start()方法启动线程(底层会自动去执行run方法)
LOGGER.info("开始执行");
aThread.start();
bThread.start();
// 4. 将耗时多的主线程放在子线程后再执行(主线程和主线程会一起交替运行)
for (int i = 0; i < 100; i++) {
System.out.println("main-" + i);
}
}
}
// 1. 定义一个子类继承线程类java.lang.Thread,重写run()方法
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);
}
}
}
3.2 多线程的创建方式二:实现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,最后观察下输出顺序
//2. 创建任务类对象
ARunnable aRunnable = new ARunnable();
BRunnable bRunnable = new BRunnable();
//3. 把任务类对象交给Thread处理
Thread thread1 = new Thread(aRunnable);
Thread thread2 = new Thread(bRunnable);
//4. 调用线程对象的start()方法启动线程
thread1.start();
thread2.start();
}
}
class ARunnable implements Runnable{
public static final Logger LOGGER = LoggerFactory.getLogger("ARunnable");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.info("A-" + i);
}
}
}
class BRunnable implements Runnable{
public static final Logger LOGGER = LoggerFactory.getLogger("BRunnable");
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LOGGER.info("B-" + i);
}
}
}
3.3 多线程的创建方式三:实现Callable接口
多线程的创建方式三:实现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方法去获取线程任务执行的结果 优缺点 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果 缺点:编码复杂一点。
FutureTask的API
FutureTask提供的构造器 | 说明 |
---|---|
public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
FutureTask提供的方法 | 说明 |
---|---|
public V get() throws Exception | 获取线程执行call方法返回的结果。 |
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//需求:启动两个子线程,分别计算100之内的奇数的和和偶数的和,然后在主线程中再做个汇总,得到总和
// 创建Callable的对象
OddTask oddTask = new OddTask();
EvenTask evenTask = new EvenTask();
// 创建FutureTask的对象
FutureTask<Integer> oddFutureTask = new FutureTask<>(oddTask);
FutureTask<Integer> evenFutureTask = new FutureTask<>(evenTask);
// 把FutureTask对象交给Thread对象
//创建线程对象
Thread oddThread = new Thread(oddFutureTask);
Thread evenThread = new Thread(evenFutureTask);
//调用线程的start方法
oddThread.start();
evenThread.start();
// 获取线程执行call方法返回的结果
Integer oddSum = oddFutureTask.get();
Integer evenSum = evenFutureTask.get();
// 在主线程中再做个汇总,得到总和
System.out.println("奇数的和为:"+oddSum);
System.out.println("偶数的和为:"+evenSum);
System.out.println("总和为:"+(oddSum+evenSum));
}
}
//计算100之内的奇数的和,并返回
class OddTask 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 EvenTask 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接口 | 扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果 | 编程相对复杂 |
3.4 Thread类的方法
Thread提供的常用方法 | 说明 |
---|---|
public void run() | 线程的任务方法 |
public void start() | 启动线程 |
public String getName() | 获取当前线程的名称,线程名称默认是Thread-索引 |
public void setName(String name) | 为线程设置名称 |
public static Thread currentThread() | 获取当前执行的线程对象 |
public static void sleep(long time) | 让当前执行的线程休眠多少毫秒后,再继续执行 |
public final void join()... | 让调用当前这个方法的线程先执行完! |
Thread提供的常见构造器 | 说明 |
---|---|
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target, String name) | 封装Runnable对象成为线程对象,并指定线程名称 |
public class Demo1 {
//需求: 通过下面任务,我们来学习sleep和join方法
//1. 创建A、B两个线程类,分别打印1~5
//2. 在主线程序中创建A、B两个子线程
//3. 在主线程序中开启A、B两个子线程
//4. 让A线程每打印一次,都要暂停3秒钟
//5. B线程要等A线程打印完毕,再开始打印
public static void main(String[] args) throws InterruptedException {
//2. 在主线程序中创建A、B两个子线程
AThread aThread = new AThread();
BThread bThread = new BThread();
//3.1 在主线程序中开启子线程A
aThread.start();
//5. B线程要等A线程打印完毕,再开始打印
aThread.join();
//3.2 在主线程序中开启子线程B
bThread.start();
}
}
//1. 创建A、B两个线程类,分别打印1~5
class AThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("A线程打印了" + i);
try {
//4. 让A线程每打印一次,都要暂停3秒钟
Thread.sleep(3000);//重写父类方法,父类不能抛出异常,子类也不可以,只能try-catch
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class BThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("B线程打印了" + i);
}
}
}