文章目录
- 1.常用类库
- 2.集合
- 3.IO
- 4.多线程
- 5.网络编程
- 6.XML和JSON
- 7.枚举、注解和反射
1.常用类库
1.1泛型
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定 义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
定义一个泛型类:
public class ClassName<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
在编译之后程序会采取去泛型化的措施。
也就是说Java中的泛型,只在编译阶段有效。
在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加 类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
泛型除了在类中使用,还可以在接口、方法等中使用。
在使用泛型时,可以指定泛型的限定区域,
- 例如: 必须是某某类的子类 或 某某接口的实现类
- 格式: <T extends 类或接口1 & 接口2>
类型通配符是使用?代替方法具体的类型实参。
- <? extends Parent> 指定了泛型类型的上界
- <? super Child> 指定了泛型类型的下界
- <?>指定了没有限制的泛型类型
Plate<t extends Apple> p = new Plate<Fruit>();
泛型的作用:
1、 提高代码复用率
2、 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
1.2 java.util.Objects
- equals
对于两个对象a,b:
a.equals(b); 若a为空则会抛出异常
Objects.equals(a,b);若a为空也可以正常比较 - isNull
判断是否为空
1.3 java.lang.Math
- abs
返回绝对值 - min
返回两个数中较小的值 - round
返回四舍五入的值
Math.round(100.5);返回101
Math.round(-100.5);返回-100 - floor
返回小于等于参数的最大整数
Math.floor(100.5);返回100
Math.floor(-100.5);返回-101 - ceil
返回大于等于参数的最大整数
Math.floor(100.5);返回101
Math.floor(-100.5);返回-100
1.3 java.util.Arrays
- toString
用于输出数组,类似格式:[1,2,4,5] - sort
从小到大排序 - binarySearch
二分查找,返回下标 - copyOf
arr = Arrays.copyOf(arr,15);
将原数组arr动态扩容为长度15的新数组
1.4 java.math.BigDecimal
解决小数运算的误差问题,比double可靠
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
BigDecimal result = b1.add(b2);//函数结果为返回结果
result.intValue();//可以将结果转化为int类型
加减乘除:add,subtract,multiply,divide
1.5 java.util.Date
- getTime
返回自此 Date对象表示的1970年1月1日00:00:00 GMT以来的毫秒数。
1.6 java.text.DateFormat
符号 | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
- format
将date对象 转换为字符串(yyyy年MM月dd日 HH:mm ss)
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm ss");
String text = format.format(new Date());
按格式输出了当前时间。
- parse
将 yyyy年MM月dd日 HH:mm ss 日期字符串 转换为date对象
Date date = format.parse("2021-12-12 12:12 12");
就可以输出date对象的getTime等。
1.7 java.util.Calendar
不是通过new创建对象,而是getInstance
- set
设置时间 - get
- add
- getTime
- getActualMaxmum
获取当前最大天数等
Calendar cl = Calendar.getInstance();
int year = cl.get(Calendar.YEAR);//获取年
int day = cl.get(Calendar.DAY_OF_YEAR);//获取今天是今年中的第几天
cl.add(Calendar.MONTH,3);//将当前月份+3
1.8 java.lang.System
- exit
终止当前运行的java虚拟机,参数为状态代码,0为正常终止,非零异常终止 - currentTimeMillis
以毫秒为单位返回当前时间
1.9 String类
字符串是不变的,它们的值在创建之后无法更改,因为字符串内部是char数组,拼接字符串是形成了一个新的字符串
运行时常量池(包含字符串常量池)存在于方法区
String text = "123123";
String text2 = "123123";
此时text==text2(相同的字符串是有缓存的)
String text3 = new String("123123");
此时不相等,new是开辟了
-
chatAt
返回指定索引处的char值 -
trim
删除前导和尾随空格后返回新字符串 -
split
分割字符串
例如:将字符串以空格分割:
String[] arr = str.split(" ");
String字符串拼接会产生垃圾且不会被回收,尽量避免
可用StringBuffer和StringBuilder:可变的字符序列
- StringBuffer.append
拼接字符串,再利用toString转成字符串
尽量不用加号拼接字符串。
2.集合
学习集合主要关注如何把数据进行增删改查操作。
2.1类集概述
类集是java对数据库成熟的实现
Java类集结构图:
2.2链表与二叉树
2.2.1链表
链表 [Linked List]:链表是由(内存地址)一组不必相连(不必相连:可以连续也可以不连续)的内
存结构(节点),按特定的顺序链接在一起的抽象数据类型。
链表节点结构:
class Node{
Object data;
Node next;
}
- 链表优点:
空间没有限制
插入删除都很快 - 链表缺点:
存储数据很慢
2.2.2二叉树
有序的二叉树:
- 先序遍历
先访问根节点,然后访问左节点,最后访问右节点(根->左->右) - 中序遍历
先访问左节点,然后访问根节点,最后访问右节点(左->根->右) - 后序遍历
先访问左节点,然后访问右节点,最后访问根节点(左->右->根)
但是上面这种结构不是有序的,今后一般都用有序的二叉树。
二叉树节点结构:
class Node{
Object data;
Node left;
Node right;
}
2.3常见数据结构
- 栈(stack)
先进后出
存数据:压栈
取数据:弹栈 - 队列(queue)
先进先出(像排队一样)
单端队列、双端队列 - 数组
查找元素快,增删元素慢 - 链表
单向链表、双向链表
查找元素慢,增删元素快 - 二叉树
一个数据有两个节点
一般是有序的
红黑树 保持两端平衡(查找元素特别快)
2.4 Collection接口
Collection是单值存储,一次只能存一个数据
但在开发中不会直接使用 Collection 接口。而使用其操作的子接口:List、Set(存储单值的集合类)
List中的元素允许重复,而Set不允许重复
2.4.1 List接口
其中,public E remove(int index)可以删除某个数据,但是可以返回删除的数据,即 可以将数据拿出并将其删除
List的实现类如ArrayList和Linkedlist用的比较多
2.4.2 ArrayList
使用的是数组结构,增加删除慢,查找快
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
System.out.println(all); // 打印all对象调用toString()方法
2.4.3 Vector
如果不需要线程安全,建议使用ArrayList来代替Vector
2.4.4 LinkedList
使用双向链表结构,对于增加删除快,查找慢(和ArrayList相反)
- addFirst
在首部添加元素 - getFirst
在首部拿到元素 - remove
检测并删除列表的头部(第一个元素)
模拟栈用push和pop(使用较少,了解):
2.4.5 Iterator与ListIterator
Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。
将数组遍历输出:
Iterator 接口本身可以完成输出的功能,但是此接口只能进行由前向后的单向输出。如果要想进行双向输出,则必须 使用其子接口 —— ListIterator。
2.4.6 forEach
增强for循环:
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
for (String str : all) {
System.out.println(str) ;
}
2.4.7 Set集合
不包含重复元素的集合。
Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。
因为此接口没有 List 接口中定义的 get(int index)方法,所以无法使用循环进行输出,只能toArray转成数组再遍历,或者用Iterator进行迭代。
2.4.8 HashSet
散列存放(哈希表),内部使用的HashMap,且不能保证存储顺序
2.4.9 TreeSet和Comparable
与 HashSet 不同的是,TreeSet 本身属于排序的子类
但是自定义的类型无法排序。如果想进行排序的话,则必须在自定义类中实现 Comparable 接口
public class Person implements Comparable<Person> {
private String name;
private int age;
public int compareTo(Person per) {
if (this.age > per.age) { return 1; }
else if (this.age < per.age) { return -1; }
else { return 0; }
}
}
2.5 Map接口
Map每次存储的是一对数据(键值对)
Map集合的键不可重复
Map:Mapping(映射,而不是地图)
HashMap,TreeMap,LinkedHashMap中add,remove等方法的使用和Map一样。
2.5.1 HashMap
基于hash表的map接口实现。
对象链表+数组 的方式存放数据
%16取余存放到对象数组(每一个数都是一个哈希桶),每一个数据都是链表,取得余数按次序存到链表中
jdk1.8之后:
哈希桶中的数据量大于8时,从链表转换为红黑二叉树;当哈希桶中的数据量减少到6时,从红黑二叉树转换为链表
初始桶数量为16,散列因子/负载因子0.75。可以扩容(resize())
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "张三A");
map.put(2, "李四");
map.put(3, "王五");
Set<Integer> set = map.keySet(); // 得到全部的key
Collection<String> value = map.values(); // 得到全部的value
Iterator<Integer> iter1 = set.iterator();
Iterator<String> iter2 = value.iterator();
System.out.print("全部的key:");
while (iter1.hasNext()) {
System.out.print(iter1.next() + "、");
}
System.out.print("\n全部的value:");
while (iter2.hasNext()) {
System.out.print(iter2.next() + "、");
}
2.5.2 map集合各子类区别
操作方法都一样:
多线程:程序的多条路径
Map子类 | 特点 |
---|---|
HashMap | 线程不安全,效率高(同时进行) |
HashTable | 线程安全,效率低(排队) |
ConcurrentHashMap | 采用分段锁机制(多个排队的队伍),线程安全,效率比较高 |
- HashMap不保证存储循序;
- TreeMap也不保证存储顺序,但是会自动从小到大排序;
- LinkedHashMap保证存储顺序
3.IO
3.1 File
文件和目录路径名的抽象表示。
File表示的对象可以没有,可以创建新文件
public static void main(String[] args) {
File e = new File("e://");
File[] files = e.listFiles();
listFile(files);
}
public static void listFile(File[] files){
if(files!=null&&files.length>0){
for (File file:files) {
if(file.isFile()){
if(file.getName().endsWith(".avi")){
if(file.length()>100*1024*1024)
System.out.println("找到了一个avi文件"+file.getAbsolutePath());
}
}else {
File[] files2 = file.listFiles();
listFile(files2);
}
}
}
}
相对路径和绝对路径:
相对路径:以盘符开始 例如c://a.txt
绝对路径: java中是相对于项目目录路径 例如 a.txt
File file1 = new File("c://a.txt");
File file2 = new File("a.txt");
System.out.println("File1的路径"+file1.getAbsolutePath());
System.out.println("File2的路径"+file2.getAbsolutePath());
3.2 IO流
3.2.1概述
可以将这种数据传输的操作看做一种数据流动,按照流动的方向分为输入Input和输出Output
Java中的IO操作主要是指java.io包下的一些常用类使用,通过这些类对数据进行读取和写出操作
3.2.2分类
按照流的方向来分,可以分为:输入流和输出流
按照流动的数据类型来分,可以分为:字节流和字符流
- 字节流:输入流:InputStream,输出流:OutputStream
- 字符流:输入流:Reader,输出流:Writer
3.2.3字节流
一切皆字节:
计算机中的任何数据(文本,图片,视频,音乐等等)都是以二进制的形式存储的
在进行数据传输时,也是以二进制的形式存储的
任何流,在传输时底层都是二进制
- close()
关闭此输出流并释放与此流关联的所有系统资源(如不释放就不能关闭的情况) - flush()
刷新此输出流并强制写出任何缓冲中的输出字节
字节输出流:
fileOutputStream:将内存中变量写进了文件里
//OutputStream
FileOutputStream fos = new FileOutputStream("c://a.txt");//文件不存在的话会主动创建(可能权限原因创建不出来)
/*byte[] bytes = {65,66,67,68,69};//字节65就是A
fos.write(bytes);*/
//byte[] bytes2 = {65,66,67,68,69};
byte[] bytes2 = "ABCDEF".getBytes();//变成字节数组(可以直接用字符流)
fos.write(bytes2,2,2);
//fos.write(bytes2);
fos.close(); //写在哪在哪关闭
System.out.println("已经写出");
字节输入流:
FileInputStream:将硬盘中的文件读取到内存中
FileInputStream fis = new FileInputStream("c://a.txt");
//一次读取一个字节
/*while (true){
byte b = (byte) fis.read();
if(b==-1){//读到了文件尾部
break;
}
System.out.println(b);
}*/
//读取一组字节
byte[] bytes = new byte[10];
int len = fis.read(bytes);//拿到读取的长度
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
fis.close();
确定文件读完:len=-1
3.2.4字符流
硬盘(磁盘):存储数据
计算机里存储的都是二进制
文字能显示是因为有编码表
能显示到显示器上因为有显卡(结构简单,进行浮点运算)
字符流解决了“读一半字”的情况,以文字为单位输出,若不足一个文字则缓存起来,加flush()刷新才会显示出来,调用close()也会调用刷新方法
字符输出流:
FileWriter
//writer
FileWriter fw = new FileWriter("c://b.txt",true);//加上true开启追加模式
//fw.write('a');
fw.append("锄禾日当午").append(",").append("汗滴禾下土"); //可以一致追加
fw.write("锄禾日当午");
fw.flush(); //刷新
fw.close();
字符输入流:
FileReader
//reader
FileReader fr = new FileReader("b.txt");
/*while (true){
int c = fr.read();
if(c==-1){
break;
}
System.out.println((char)c);
}*/
char[] chars = new char[100];
int len = fr.read(chars);
String text = new String(chars,0,len);//读到0到长度为len
System.out.println(text);
fr.close();
3.2.5转换流
将字节流 装饰 为字符流:使用装饰者设计模式
FileInputStream fis = new FileInputStream("c://a.txt");
//将字节输入流转换为字符输入流 参数为要转换的字节流
InputStreamReader isr = new InputStreamReader(fis,"gbk");
while (true){
int c = isr.read();
if(c==-1){
break;
}
System.out.println((char) c);
}
打印流和缓存流(Print和BufferedReader):
3.3收集异常日志
后期不需要自己写,可以使用框架
try {
String s = null;
s.toString();
}catch (Exception e){
PrintWriter pw = new PrintWriter("c://bug.txt");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
pw.print(format.format(new Date()));
e.printStackTrace(pw);
pw.close();
}
3.4 properties
//properties文件与properties类
/*Properties pt = new Properties();
//键=值
pt.put("name","金苹果");
pt.put("info","讲述历了金苹果种植的过程");
FileWriter fw = new FileWriter("c://book.properties");
pt.store(fw,"存储的图书");
fw.close();*/
Properties pt = new Properties();
Reader fw = new FileReader("c://book.properties");
pt.load(fw);
System.out.println("name");
System.out.println("info");
3.5序列化技术
序列化:将对象存到文件里面
反序列化:将文件里的对象读取出来
public static void main(String[] args) throws IOException {
//序列化与反序列化
/*序列化
Book b = new Book("金苹果","讲述了种植过程");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c://book.hahas"));
oos.writeObject(b);
oos.close();*/
//反序列化
ObjectInputStream ois = new ObjectInputStream(new ObjectInputStream("c://book.txt"));
Book o = (Book)ois.readObject();
System.out.println(o.getInfo());
}
//Serializable为标记,实现这个接口就是为了打一个标记
static class Book implements Serializable {
private String name;
private String info;
public Book(String name, String info) {
this.name = name;
this.info = info;
}
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", info='" + info + '\'' +
'}';
}
}
3.6 try-with-resources
/*try {
FileReader fr = new FileReader("c://book.txt");
int c = fr.read();
System.out.println((char)c);
fr.close();
} catch (IOException e) {
e.printStackTrace();
}*/
//jdk9
FileReader fr = new FileReader("c://book.txt");
PrintWriter pw = new PrintWriter("c://book.txt");
try(fr;pw){
int c = fr.read();
System.out.println((char)c);
}catch (IOException e) {
e.printStackTrace();
}
3.7 Externalizable接口
Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程。
通过序列化和反序列化实现网络传输、本地存储的目的。
3.7.1 Serializable实现Java序列化
要实现Java对象的序列化,只要将类实现标识接口——Serializable接口即可,不需要我们重写任何方
法就可以实现序列化。
实体类:
public class Student implements Serializable {
private String stuNum;
private String stuName;
/*** 教师姓名:一个学生可以有多个老师 */
private List<String> teacherList;
public Student() {
}
public Student(String stuNum, String stuName, List<String> teacherList) {
this.stuNum = stuNum;
this.stuName = stuName;
this.teacherList = teacherList;
}
@Override
public String toString() {
return "Student{" + "stuNum='" + stuNum + '\'' + ", stuName='" + stuName + '\'' + ", teacherList=" + teacherList + '}';
}
public String getStuNum() {
return stuNum;
}
public void setStuNum(String stuNum) {
this.stuNum = stuNum;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public List<String> getTeacherList() {
return teacherList;
}
public void setTeacherList(List<String> teacherList) {
this.teacherList = teacherList;
}
}
编写Java对象序列化和反序列化工具类:
public class MySerializeUtil {
/*** 将对象序列化到指定文件中 *
* @param obj
* @param fileName
*/
public static void mySerialize(Object obj, String fileName) throws IOException {
OutputStream out = new FileOutputStream(fileName);
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
objOut.close();
}
/*** 从指定文件中反序列化对象 *
* @param fileName
* @return
*/
public static Object myDeserialize(String fileName) throws IOException, ClassNotFoundException {
InputStream in = new FileInputStream(fileName);
ObjectInputStream objIn = new ObjectInputStream(in);
Object obj = objIn.readObject();
return obj;
}
}
测试对象的序列化和反序列化:
public class MainTest {
public static void main(String[] args) {
List<String> teacherList = new ArrayList<>();
teacherList.add("空空道人");
teacherList.add("贾代儒");
Student stu1 = new Student("1001", "贾宝玉", teacherList);
System.out.println("原始对象:" + stu1);
String fileName = "stu01.txt";
try {//对象序列化
MySerializeUtil.mySerialize(stu1, fileName);
System.out.println("序列化原始对象完成!OK!"); //对象的反序列化
Object obj = MySerializeUtil.myDeserialize(fileName);
if (obj instanceof Student) {
Student stuNew = (Student) obj;
System.out.println("反序列化之后的对象:" + stuNew);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.7.2部分属性的序列化
3.7.2.1 使用transient修饰符
修改实体类,将实体类中不想序列化的属性添加transient修饰词。
public class Student implements Externalizable {
private String stuNum;
private transient String stuName;
private transient List<String> teacherList;
......
3.7.2.2 使用static修饰符
static修饰符修饰的属性也不会参与序列化和反序列化。
修改实体类,将实体类中不想序列化的属性添加static修饰词。
public class Student implements Externalizable {
private String stuNum;
private static String stuName;
private List<String> teacherList;
......
修改测试类,在完成原始对象的序列化之后再对static修饰的变量进行一次赋值操作。
3.7.2.3 默认方法writeObject和readObject
修改实体类,将transient修饰词去掉,添加两个方法。
public class Student implements Serializable {
private String stuNum;
private String stuName;
private List<String> teacherList;
private void writeObject(ObjectOutputStream objOut) throws IOException {
System.out.println("writeObject-----------");
objOut.writeObject(stuNum);
objOut.writeObject(stuName);
}
private void readObject(ObjectInputStream objIn) throws IOException, ClassNotFoundException {
System.out.println("readObject-----------");
stuNum = (String) objIn.readObject();
stuName = (String) objIn.readObject();
}
......
3.7.3 Externalizable实现Java序列化
Externalizable继承自Serializable,使用Externalizable接口需要实现readExternal方法和
writeExternal方法来实现序列化和反序列化。
public class Student implements Externalizable {
private String stuNum;
private String stuName;
/*** 教师姓名:一个学生可以有多个老师 */
private List<String> teacherList;
public Student() {
}
public Student(String stuNum, String stuName, List<String> teacherList) {
this.stuNum = stuNum;
this.stuName = stuName;
this.teacherList = teacherList;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(stuNum);
out.writeObject(stuName);
//out.writeObject(teacherList);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
stuNum = (String) in.readObject();
stuName = (String) in.readObject();
//teacherList= (List<String>) in.readObject();
}
@Override
public String toString() {
return "Student{" + "stuNum='" + stuNum + '\'' + ", stuName='" + stuName + '\'' + ", teacherList=" + teacherList + '}';
}
public String getStuNum() {
return stuNum;
}
public void setStuNum(String stuNum) {
this.stuNum = stuNum;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public List<String> getTeacherList() {
return teacherList;
}
public void setTeacherList(List<String> teacherList) {
this.teacherList = teacherList;
}
}
3.7.4 Serializable VS Externalizable
区 别 | Serializable | Externalizable |
---|---|---|
实现复杂度 | 实现简单,Java对其有内建支持 | 实现复杂,由开发人员自己完成 |
执行效率 | 所有对象由Java统一保存,性能较低 | 开发人员决定哪个对象保存,可能造成速度提升 |
保存信息 | 保存时占用空间大 | 部分存储,可能造成空间减少 |
使用频率 | 高 | 偏低 |
4.多线程
4.1线程与进程
进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
线程
- 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。
- 一个进程最少有一个线程。
- 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
4.2线程调度
分时调度
- 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
- 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
- CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。
4.3同步与异步
同步:排队执行 , 效率低但是安全。
异步:同时执行 , 效率高但是数据不安全。
4.4并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
4.5开启多线程技术
4.5.1继承Thread
每个线程都有自己的栈空间,共用一份堆内存。
子线程继承Thread:
public class MyThread extends Thread{
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
//这里的代码就是一条新的执行路径
//这个执行路径的触发方式,不是调用run方法,而是通过Thread对象的start()来启动任务
for (int i=0;i<10;i++){
System.out.println("锄禾日当午"+i);
}
}
}
主线程调用:
public class Demo {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for (int i=0;i<10;i++){
System.out.println("汗滴禾下土"+i);
}
}
}
或者使用匿名内部类:
public class Demo2 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("一二三四五"+i);
}
}
}.start();
for (int i=0;i<10;i++){
System.out.println("六七八九十"+i);
}
}
}
4.5.2实现Runnable
用于给线程进行执行的任务:
/**
* 用于给线程进行执行的任务
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
//线程的任务
for (int i=0;i<10;i++){
System.out.println("床前明月光"+i);
}
}
}
主线程:
public class Demo {
public static void main(String[] args) {
//实现Runnable
//1.创建一个任务对象
MyRunnable r = new MyRunnable();
//2.创建一个线程,并且为其分配一个任务
Thread t = new Thread(r);
//3.执行这个线程
for (int i=0;i<10;i++){
System.out.println("疑是地上霜"+i);
}
}
}
实现Runnable与继承Thread相比有如下优势:
- 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况;
- 可以避免单继承所带来的的局限性;
- 任务与线程本身是分离的,提高了程序的健壮性;
- 后序学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程。
4.6 Thread类
- setPriority(int newPriority)更改线程的优先级
- 杀死线程不用stop()方法,而是通过变量做标记通知要结束线程的地方,run方法执行结束,直接return run()方法就可以了。
- sleep(long millis)暂时停止执行线程
4.7设置和获取线程名称
public class Demo3 {
public static void main(String[] args) {
//如何获取线程的名称
System.out.println(Thread.currentThread().getName());
new Thread(new MyRunnable(),"锄禾日当午").start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
4.8线程的休眠
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
//线程的休眠
for (int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(1000);//每隔一秒钟休息一次
}
}
}
4.9线程阻塞
所有耗时间的操作都是线程阻塞,比如文件读取、接收用户输入等等。
4.10线程中断
一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。
通过给线程做标记的方式决定线程中断。
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for (int i=1;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
//给线程t1添加中断标记
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
// e.printStackTrace();
System.out.println("发现了中断标记,这个线程自杀");
return;
}
}
}
}
}
可在发现中断标记后,将资源释放(交代后事),再线程自杀。
4.11守护线程
线程:分为守护线程和用户线程
用户线程:当一个进程不包含任何的存活用户线程时,进行结束。
守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
直接创建的线程都是用户线程。
将用户线程设置为守护线程,只需setDaemon(true)即可。
Thread t1 = new Thread(new MyRunnable());
//设置t1为守护线程
t1.setDaemon(true);
t1.start();
4.12线程安全
4.12.1线程不安全问题
public class ThreadSafety {
public static void main(String[] args) {
//线程不安全
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (count>0){
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:"+count);
}
}
}
}
此时余票可能出现负数。
4.12.2线程安全1-同步代码块
public class Safe1 {
public static void main(String[] args) {
//线程不安全
//解决方案1:同步代码块
//格式:synchronized(锁对象){
//
// }
Runnable run = new Safe1.Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable {
//票数
private int count = 10;
private Object o = new Object();//同一把锁
@Override
public void run() {
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
}else {
break;
}
}
}
}
}
}
4.12.2线程安全2-方法同步
public class Safe2 {
public static void main(String[] args) {
//线程不安全
Runnable run = new Safe2.Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag){
break;
}
}
}
//给方法添加synchronized即可排队执行
public synchronized boolean sale(){
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
return true;
}
return false;
}
}
}
4.12.3线程安全3-显式锁Lock
同步代码块和同步方法都是隐式锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Safe3 {
public static void main(String[] args) {
//线程不安全
Runnable run = new Safe3.Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
//显式锁
private Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
4.13公平锁与非公平锁
公平锁就是线程先来先到,非公平锁是所有线程一起抢锁
以上三种线程安全的方法用的都是非公平锁
变为公平锁:
在用显式锁的时候fair参数为true
private Lock l = new ReentrantLock(true);
4.14线程死锁
public class Dead {
public static void main(String[] args) {
//线程死锁
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
//罪犯
static class Culprit{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯被放走了,罪犯也放了人质");
}
}
//警察
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人质,我放过你");
c.fun();
}
public synchronized void fun(){
System.out.println("警察救了人质,但是罪犯跑了");
}
}
}
4.15多线程通信-生产者与消费者问题
public class Multi {
/**
* 多线程通信问题, 生产者与消费者问题
*
* @param args
*/
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师-生产者
static class Cook extends Thread {
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
f.setNameAndSaste("老干妈小米粥", "香辣味");
} else {
f.setNameAndSaste("煎饼果子", "甜辣味");
}
}
}
}
//服务生-消费者
static class Waiter extends Thread {
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food {
private String name;
private String taste;
//true表示可以生产,生产完毕交给服务员改为false
private boolean flag = true;
public synchronized void setNameAndSaste(String name, String taste) {
if (flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();//唤醒当前睡着的所有线程
try {
this.wait();//厨师线程睡着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get() {
if (!flag) {
System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
flag = true;//让厨师开始生产
this.notifyAll();//将厨师唤醒
try {
this.wait();//服务员睡着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4.16线程的六种状态
new:尚未启动
Runnable:正在执行
Blocked:被阻塞
Waiting:无限等待(被休眠)
TimeWaiting:计时等待(等多少秒后醒过来)
Terminated:被终止
4.17带返回值的线程Callable
4.17.1 Runnable 与 Callable
//Callable接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}
4.17.2 Callable使用步骤
1.编写类实现Callable接口,实现call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
2.创建FutureTask对象,并传入第一步编写的Callable类对象
FutureTask<Integer> future=new FutureTask<>(callable);
3.通过Thread,启动线程
new Thread(future).start();
4.17.3 Runnable 与 Callable的异同
相同点
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
不同点
- Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
4.17.4 Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
例子:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCall {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> c = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
Integer j = task.get();
System.out.println("返回值为:"+j);
for (int i=0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// Thread.sleep(3000);
for (int i=0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
return 100;
}
}
}
4.18线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
4.18.1缓存线程池
缓存线程池(长度无限制)
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在,则创建线程 并放入线程池, 然后使用
public class test {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(); //向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
}
}
4.18.2定长线程池
定长线程池(长度是指定的数值)
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
- 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
public class test {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
}
}
4.18.3单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
单线程线程池
执行流程:
- 判断线程池 的那个线程 是否空闲
- 空闲则使用
- 不空闲,则等待 池中的单个线程空闲后 使用
public class test {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:" + Thread.currentThread().getName());
}
});
}
}
4.18.4周期性任务定长线程池
周期任务 定长线程池.
执行流程:
- 判断线程池是否存在空闲线程
- 存在则使用
- 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
- 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 *周期性任务执行时: 定时执行, 当某个时机触发时, 自动执行某任务 .
public class test {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/*** 定时执行
* 参数1. runnable类型的任务
* 参数2. 时长数字
* 参数3. 时长数字的单位 */
/*service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿");
}
},5,TimeUnit.SECONDS);*/
/*** 周期执行 *
* 参数1. runnable类型的任务
* 参数2. 时长数字(延迟执行的时长)
* 参数3. 周期时长(每次执行的间隔时间)
* 参数4. 时长数字的单位 */
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿");
}
}, 5, 2, TimeUnit.SECONDS);
}
}
4.19 Lambda表达式
public class Lambda {
public static void main(String[] args) {
//冗余的Runnable代码
/* Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
});
t.start();*/
Thread t = new Thread(() -> {
System.out.println("锄禾日当午");
});
t.start();
}
}
自定义接口:
public class MyLambda {
public static void main(String[] args) {
/*print(new MyMath() {
@Override
public int sum(int x, int y) {
return x+y;
}
},100,200);*/
print((int x,int y) -> {
return x+y;
},100,200);
}
public static void print(MyMath m,int x,int y){
int num = m.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
}
5.网络编程
5.1网络常识
-
什么是计算机网络
分布在不同地域的计算机, 通过硬件等网络设备使用通信线路互相连接形成的一个网格系统. 计算机网络, 可以很方便的进行 信息的传递, 资源的共享 ! -
什么是计算机的IP地址
IP地址 是计算机在互联网中的唯一标识 .
就像人在社会中的身份证号码.
本机IP: 127.0.0.1 localhost -
什么是 网络中 网站的域名
域名可以简单的理解为, IP地址的别名.
更方便记忆, 当输入域名后(例如www.baidu.com) , 计算机会访 问域名解析商 , 然后得到ip地址, 再进行访问. -
什么是计算机的端口号
端口号的范围 0-65535 之间 . *****
与ip地址很相似, IP地址是计算机在网络中的唯一标识 .
端口号是计算机中 程序的标识 . 用于在一台计算机中区分不同的应用程序
端口号在使用时 , 应尽量避免0-1024之间的端口号, 因为已经被一些知名的软件 和 windows操作系统所占 用了. -
什么是计算机之间的通信协议
是计算机与计算机之间交流的标准 .
是对数据的 传输速率, 传入接口, 步骤控制 出错控制 等等 制定的一套标准 !常用的通信协议:
http协议 : 超文本传输协议 . 80端口号
https协议: 安全的超文本传输协议 443端口号
ftp协议: 文件传输协议 21端口号
TCP协议: 传输控制协议
UDP协议: 数据报协议
5.2网络编程程序的分类
B/S 程序 : 浏览器与服务器程序(可以实时更新程序)
C/S 程序 : 客户端与服务器程序(需要客户确认才能更新程序)
5.3TCP程序
需要使用到两个类, 来编写TCP协议的CS程序 . 1.ServerSocket 搭建服务器
2.Socket 搭建客户端(接收服务器)
两方使用socket(套接字 , 通信端点) 进行交流
服务器端程序:
public class ServerDemo {
//服务器
public static void main(String[] args) throws IOException {
//搭建服务器
ServerSocket server = new ServerSocket(33333);
System.out.println("服务器启动完毕");
//等等客户端的连接
Socket socket = server.accept();
System.out.println("一个客户端连接了");
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("欢迎你连接服务器");
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String text = br.readLine();
System.out.println("服务器接收到客户端的回复:"+text);
System.out.println("服务器程序执行结束");
}
}
客户端程序:
public class ClientDemo {
//客户端
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",33333);
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String text = br.readLine();
System.out.println("客户端接收到消息:"+text);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("服务器你好");
}
}
5.4在服务器中加入多线程
public class ServerDemo {
//服务器
public static void main(String[] args) throws IOException {
//搭建服务器
ServerSocket server = new ServerSocket(33333);
System.out.println("服务器启动完毕");
//等等客户端的连接
while (true) {
Socket socket = server.accept();
new Thread(){
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
System.out.println("一个客户端连接了");
}
}
}
6.XML和JSON
6.1 XML
6.1.1简介
可扩展标记语言(eXtensible Markup Language)。
特性:
- xml具有平台无关性(Windows/Linux都可以), 是一门独立的标记语言(Java/C都可以解析).
- xml具有自我描述性
作用:
- 网络数据传输
- 数据存储
- 配置文件
6.1.2 语法
XML文档声明 <?xml version="1.0" encoding="UTF-8"?>
标记 ( 元素 / 标签 / 节点)
XML文档,由一个个的标记组成.
语法:
开始标记(开放标记): <标记名称>
结束标记(闭合标记): </标记名称>
标记名称: 自定义名称,必须遵循以下命名规则:
1.名称可以含字母、数字以及其他的字符
2.名称不能以数字或者标点符号开始
3.名称不能以字符 “xml”(或者 XML、Xml)开始
4.名称不能包含空格,不能包含冒号(:)
5.名称区分大小写
标记内容: 开始标记与结束标记之间 ,是标记的内容.
例如 ,我们通过标记, 描述一个人名:
<name>李四</name>
6.1.3解析
解析本地文件:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
public class Xml {
//解析本地文件
public static void main(String[] args) throws IOException, DocumentException {
//获取输入流
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\第四章\\4.8\\课件与笔记\\代码\\XML_JSONDemo\\src\\Demo1.xml");
//创建xml读取对象
SAXReader sr = new SAXReader();
//读取并得到文档对象
Document doc = sr.read(fis);
//通过文档获取根元素
Element root = doc.getRootElement();
//开始解析元素
System.out.println(root.getName());
//操作
// Element book = root.element("book");
// Element name = book.element("name");
// System.out.println(name.getText());
List<Element> es = root.elements();
for (int i=0;i<es.size();i++){
Element book = es.get(i);
System.out.println(book.attributeValue("id"));
System.out.println(book.elementText("name"));
System.out.println(book.elementText("info"));
System.out.println("----------------------");
}
fis.close();
}
}
解析网络文件:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class Xml2 {
//解析网络文件
public static void main(String[] args) throws IOException, DocumentException {
String phone = "18516955565";
//1. 获取到XML资源的输入流
URL url = new URL("http://apis.juhe.cn/mobile/get? phone=" + phone + "&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
//2. 创建一个XML读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象 读取XML数据,并返回文档对象
Document doc = sr.read(is);
//4. 获取根节点
Element root = doc.getRootElement();
//5. 解析内容
String code = root.elementText("resultcode");
if ("200".equals(code)) {
Element result = root.element("result");
String province = result.elementText("province");
String city = result.elementText("city");
if (province.equals(city)) {
System.out.println("手机号码归属地为:" + city);
} else {
System.out.println("手机号码归属地为:" + province + " " + city);
}
} else {
System.out.println("请输入正确的手机号码");
}
}
}
6.1.4 XPATH解析XML
通过路径快速的查找一个或一组元素
路径表达式:
- / : 从根节点开始查找
- // : 从发起查找的节点位置 查找后代节点 ***
- . : 查找当前节点
- . . : 查找父节点
- @ : 选择属性. *
属性使用方式:
[@属性名=‘值’]
[@属性名>‘值’]
[@属性名<‘值’]
[@属性名!=‘值’]
books: 路径: //book[@id=‘1’]//name
解析本地文件:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
public class Xml3 {
public static void main(String[] args) throws IOException, DocumentException {
//1. 获取输入流
FileInputStream fis = new FileInputStream("c://Demo1.xml");
//2. 创建XML读取对象
SAXReader sr = new SAXReader();
//3. 读取并得到文档对象
Document doc = sr.read(fis);
//4. 通过文档对象+xpath,查找所有的name节点
/*List<Node> names = doc.selectNodes("//book[@id='1001']//name");
for (int i=0;i<names.size();i++){
System.out.println(names.get(i).getName());
System.out.println(names.get(i).getText());
}*/
Node n = doc.selectSingleNode("//book[@id='1002']//name");
System.out.println(n.getName()+":"+n.getText());
fis.close();
}
}
解析网络文件:
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class Xml4 {
public static void main(String[] args) throws IOException, DocumentException {
String phone = "18313935565";
//1. 获取到XML资源的输入流
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
//2. 创建一个XML读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象 读取XML数据,并返回文档对象
Document doc = sr.read(is);
Node node = doc.selectSingleNode("//company");
System.out.println("运营商:"+node.getText());
is.close();
}
}
6.1.5 XML生成
步骤:
- 通过文档帮助器 (DocumentHelper) , 创建空的文档对象
Document doc = DocumentHelper.createDocument();
- 通过文档对象, 向其中添加根节点
Element root = doc.addElement("根节点名称");
- 通过根节点对象root , 丰富我们的子节点
Element e = root.addElement("元素名称");
- 创建一个文件输出流 ,用于存储XML文件
FileOutputStream fos = new FileOutputStream("要存储的位置");
- 将文件输出流, 转换为XML文档输出流
XMLWriter xw = new XMLWriter(fos);
- 写出文档
xw.write(doc);
- 释放资源
xw.close();
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class Demo5 {
public static void main(String[] args) throws IOException {
//1. 通过文档帮助器,创建一个文档对象
Document doc = DocumentHelper.createDocument();
//2. 给文档添加第一个节点(根节点)
Element books = doc.addElement("books");
//3. 通过根节点, 丰富子节点
for(int i=0;i<100;i++){
Element book = books.addElement("book");
Element name = book.addElement("name");
name.setText(i+"种苹果的小姑娘~");
Element info = book.addElement("info");
info.setText(i+"辛勤种植苹果的故事~");
book.addAttribute("id",100+i+"");
}
//4. 创建一个文件输出流
FileOutputStream fos = new FileOutputStream("c://books.xml");
//5. 将输出流转换为XML输出流
XMLWriter xw = new XMLWriter(fos);
//6. 写出文档
xw.write(doc);
//7. 释放资源
xw.close();
System.out.println("执行完毕");
}
}
6.2 JSON
6.2.1简介
简介:
JSON: JavaScript Object Notation JS对象简谱 , 是一种轻量级的数据交换格式.
对象格式:
一个对象, 由一个大括号表示.
括号中 描述对象的属性 .
通过键值对来描述对象的属性 (可以理解为, 大括号中, 包含的是一个个的键值对.)
格式:
键与值之间使用冒号连接, 多个键值对之间使用逗号分隔.
键值对的键 应使用引号引住 (通常Java解析时, 键不使用引号会报错. 而JS能正确解析.)
键值对的值, 可以是JS中的任意类型的数据
6.2.2解析
利用Gson解析
import com.google.gson.Gson;
public class Json {
public static void main(String[] args) {
//创建Gson对象
Gson g = new Gson();
//转换
//把对象转为JSON
// Book b = new Book("100","金苹果","种植苹果");
// String s = g.toJson(b);
// System.out.println(s);
//把JSON转为对象
Book b = g.fromJson("{\"id\":\"100\",\"name\":\"金苹果\",\"info\":\"种植苹果\"}", Book.class);
System.out.println(b.getId());
}
}
转成list:
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.List;
public class Json2 {
public static void main(String[] args) {
//创建Gson对象
Gson g = new Gson();
//转换 {"id":"100","name":"金苹果","info":"种植苹果","page":["锄禾日当午","汗滴禾下土"]}
HashMap data = g.fromJson("{\"id\":\"100\",\"name\":\"金苹果\",\"info\":\"种植苹果\",\"page\":[\"锄禾日当午\",\"汗滴禾下土\"]}", HashMap.class);
// System.out.println(data.get("page"));
List page = (List) data.get("page");
System.out.println(page.get(1));
}
}
利用FastJSON解析
import com.alibaba.fastjson.JSON;
import java.util.List;
public class Json3 {
public static void main(String[] args) {
Book book = new Book("1000","唐诗三百首","床前明月光,疑是地上霜");
//转换
//对象转成JSON {"id":"1000","info":"床前明月光,疑是地上霜","name":"唐诗三百首"} ["123","234"]
String json = JSON.toJSONString(book);
System.out.println(json);
//JSON转成对象
// Book book1 = JSON.parseObject("{\"id\":\"1000\",\"info\":\"床前明月光,疑是地上霜\",\"name\":\"唐诗三百首\"}", Book.class);
// System.out.println(book1.getId());
List<String> strings = JSON.parseArray("[\"123\",\"234\"]", String.class);
System.out.println(strings.get(1));
}
}
7.枚举、注解和反射
7.1枚举
7.1.1定义
枚举定义格式:
public enum Enum {
LOW(30), MEDIUM(15), HIGH(7), URGENT(1);
private int levelValue;
private Enum(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
}
7.1.2主要方法
用的多的是equals,可用在switch中选择
public class EnumDemo {
public static void main(String[] args) {
System.out.println(EnumMy.LOW.getLevelValue());
System.out.println(EnumMy.LOW.compareTo(EnumMy.HIGH));
System.out.println(EnumMy.LOW.name());
System.out.println(EnumMy.LOW.toString());
System.out.println(EnumMy.LOW.ordinal());
EnumMy x = Enum.valueOf(EnumMy.class, "HIGH");
System.out.println(x.name());
}
public static void ha(EnumMy e){
switch (e){
case LOW:break;
case HIGH:break;
}
}
}
7.1.3枚举接口
所有的枚举都继承自java.lang.Enum类。
由于Java 不支持多继承,所以枚举对象不能再继承其他类。
每个枚举对象,都可以实现自己的抽象方法
interface LShow {
void show();
}
public enum Level implements LShow {
LOW(30) {
@Override
public void show() {
System.out.println("低级");
}
}, MEDIUM(15) {
@Override
public void show() {
System.out.println("中级");
}
}, HIGH(7) {
@Override
public void show() {
System.out.println("高级");
}
}, URGENT(1) {
@Override
public void show() { //...
}
};
private int levelValue;
private Level(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
}
7.1.4注意事项
一旦定义了枚举,最好不要妄图修改里面的值,除非修改是必要的。
枚举类默认继承的是java.lang.Enum类而不是Object类
枚举类不能有子类,因为其枚举类默认被final修饰
只能有private构造方法
switch中使用枚举时,直接使用常量名,不用携带类名
不能定义name属性,因为自带name属性
不要为枚举类中的属性提供set方法,不符合枚举最初设计初衷。
7.2注解
7.2.1简介
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。(给机器看的注释)
Java 语言中的类、方法、变量、参数和包等都可以被标注。和注释不同,Java 标注可以通过反射获取标注内容。
在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
主要用于:
- 编译格式检查
- 反射中解析
- 生成帮助文档
- 跟踪代码依赖 等
7.2.2内置注解
系统自带的注解
@Override : 重写 *
定义在java.lang.Override
@Deprecated:废弃 *但是方法也可以用
定义在java.lang.Deprecated
@SafeVarargs
Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface: 函数式接口 *
Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable:标识某注解可以在同一个声明上使用多次
Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
SuppressWarnings:抑制编译时的警告信息。 *
定义在java.lang.SuppressWarnings
三种使用方式:
- @SuppressWarnings(“unchecked”) [^ 抑制单类型的警告]
- @SuppressWarnings(“unchecked”,“rawtypes”) [^ 抑制多类型的警告]
- @SuppressWarnings(“all”) [^ 抑制所有类型的警告]
public class Zhujie {
@Override
public String toString(){
return super.toString();
}
public static void main(String[] args) {
@SuppressWarnings("all")
Person p = new Person();
// p.setAge(18);
p.setAge2(18);
}
}
class Person{
private int age;
public int getAge() {
return age;
}
/**
* 此方法已被废弃,请通过setAge2操作
* @param age
*/
@Deprecated
public void setAge(int age) {
this.age = age;
}
public void setAge2(int age) {
if (age<0 || age>150){
throw new RuntimeException("年龄不合理");
}
this.age = age;
}
}
7.2.3元注解
元注解:作用在其他注解的注解
元注解有哪些:
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可
以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中 javadoc。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是自动继承的
- 子类会继承父类使用的注解中被@Inherited修饰的注解
- 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有 被@Inherited修饰
- 类实现接口时不会继承任何接口中定义的注解
7.2.4自定义注解
定义格式:@interface自定义注解名{}
注意事项
- 定义的注解,自动继承了java.lang,annotation.Annotation接口
- 注解中的每一个方法,实际是声明的注解配置参数
方法的名称就是 配置参数的名称
方法的返回值类型,就是配置参数的类型。只能是:基本类型/Class/String/enum - 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值。
import java.lang.annotation.*;
@MyAnnotation(value="张三",num=100)
public class Zhujie2 {
public static void main(String[] args) {
}
public void add(){
}
}
//注解是否包含在文档中
@Documented
//用途类型
@Target({ElementType.TYPE,ElementType.METHOD})
//保存策略
@Retention(RetentionPolicy.RUNTIME)
//可以继承
@interface MyAnnotation{
String value();//只有value关键字可以在注解传值时候省略
int num();
int num2() default 100;//可以不传参数
}
7.3反射
7.3.1概述
JAVA反射机制是在运行状态中,获取任意一个类的结构 , 创建对象 , 得到方法,执行方法 , 属性;
这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
7.3.2类加载器
Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分, 负责动态加载Java类到Java虚拟机的内存空间中。
java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。
BootstrapClassLoader(引导启动类加载器):
嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,引 导启动类加载器无法被应用程序直接使用。
ExtensionClassLoader(扩展类加载器): ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。
是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类 库。
它的父加载器是BootstrapClassLoader App ClassLoader(应用类加载器):
App ClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文 件。它的父加载器为Ext ClassLoader
public class Fanshe {
public static void main(String[] args) throws IOException {
InputStream is = Fanshe.class.getClassLoader().getResourceAsStream("config.txt");//如果没有source文件夹就加载src目录下的
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String text = br.readLine();
System.out.println(text);
br.close();
}
}
7.3.3 Class与加载方式
要想了解一个类,必须先要获取到该类的字节码文件对象。
在Java中,每一个字节码文件,被夹在到内存后,都存在一个对应的Class类型的对象。
得到class的几种方式:
- 如果在编写代码时, 指导类的名称, 且类已经存在, 可以通过
包名.类名.class 得到一个类的 类对象 - 如果拥有类的对象, 可以通过
Class 对象.getClass() 得到一个类的类对象 - 如果在编写代码时, 知道类的名称 , 可以通过
Class.forName(包名+类名): 得到一个类的 类对象
public class MyClass {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式,通过包名.类名.class加载类
Class<Person> c1 = Person.class;
System.out.println(c1);
//第二种方式,通过类的对象获取类的信息
Person p = new Person();
Class<Person> c2 = (Class<Person>) p.getClass();
System.out.println(c2);
System.out.println(c1==c2);
//第三种方式(动态编程),去掉Person类不报错,执行时才报错
Class<Person> c3 = (Class<Person>) Class.forName("Person");
System.out.println(c3);
System.out.println(c1==c2 && c1==c3);
}
}
7.3.4反射中的构造方法
- 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组)
例如:构造方法如下: Person(String name,int age)
得到这个构造方法的代码如下: Constructor c = p.getClass().getConstructor(String.class,int.class); - 获取构造方法数组 getConstructors();
- 获取所有权限的单个构造方法 getDeclaredConstructor(参数类型的class对象数组)
- 获取所有权限的构造方法数组 getDeclaredConstructors();
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class MyClass2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Person> pClass = (Class<Person>) Class.forName("Person");
//找到无参构造方法
Constructor<Person> c1 = pClass.getConstructor();
//使用无参构造方法,创建对象
Person p = c1.newInstance();
System.out.println(p);
//找到包含有参构造方法
Constructor<Person> c2 = pClass.getConstructor(String.class, int.class);
//使用这个构造方法创建对象
Person p2 = c2.newInstance("张三", 19);
System.out.println(p2);
//拿到私有构造方法
Constructor<Person> c3 = pClass.getDeclaredConstructor(String.class);
c3.setAccessible(true);//忽略权限
Person p3 = c3.newInstance("李四");
System.out.println(p3);
}
}
7.3.5反射中的方法
- getMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(public修饰的) - getMethods();
得到一个类的所有方法 (public修饰的) - getDeclaredMethod(String methodName , class… clss)
根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认) - getDeclaredMethods();
得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyClass3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//加载类
Class<?> c1 = Class.forName("Person");
//获取类的构造方法
Constructor c = c1.getConstructor();
//创建对象
Object o = c.newInstance();
//获取类的方法
Method setName = c1.getMethod("setName", String.class);
Method setAge = c1.getDeclaredMethod("setAge", int.class);
setAge.setAccessible(true);
//参数1:哪个对象要执行setName方法
//参数2:调用方法时传递的参数 0-n
setName.invoke(o, "张三");
setAge.invoke(o,18);
System.out.println(o);
}
}
7.3.6反射中的属性
- getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
- getDeclaredFields() 获取所有属性
- getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
- getFields() 获取所有属性 (public)
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class MyClass4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//加载类
Class c = Class.forName("Person");
//获取类的构造方法
Constructor ct = c.getConstructor();
//创建对象
Object o = ct.newInstance();
Field phoneNumber = c.getField("phoneNumber");
phoneNumber.set(o,"1999999999");
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(o,"张三");
System.out.println(o);
}
}
7.3.7反射与注解
案例:
import java.lang.annotation.*;
@Target(ElementType.TYPE)//注解可以使用在类上
@Retention(RetentionPolicy.RUNTIME)//持久化策略
@Documented//可以写在文档里
public @interface TableAnnotation {
/**
* 用于标注类对应的表格名称
* @return
*/
String value();
}
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)//持久化策略
@Target(ElementType.FIELD)//注解作用在属性上
@Documented//可以写在文档里
public @interface ColumnAnnotation {
/**
* 描述列名
* @return
*/
String columnName();
/**
* 描述类型
* @return
*/
String type();
/**
* 描述数据的长度
* @return
*/
String length();
}
import java.util.Objects;
@TableAnnotation("test_Book")//类对应数据库中的test_Book表格
public class MyBook {
@ColumnAnnotation(columnName = "id",type = "int",length = "11")
private int id;
@ColumnAnnotation(columnName = "name",type = "varchar",length = "50")
private String name;
@ColumnAnnotation(columnName = "info",type = "varchar",length = "1000")
private String info;
public MyBook(int id, String name, String info) {
this.id = id;
this.name = name;
this.info = info;
}
public MyBook() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyBook book = (MyBook) o;
return Objects.equals(id, book.id) && Objects.equals(name, book.name) && Objects.equals(info, book.info);
}
@Override
public int hashCode() {
return Objects.hash(id, name, info);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
import java.lang.reflect.Field;
public class MyClass5 {
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("MyBook");
/* Annotation[] as = c.getAnnotations();//获取类上所有注解
for (Annotation a:as){
System.out.println(a);
}*/
TableAnnotation ta = (TableAnnotation) c.getAnnotation(TableAnnotation.class);
String value = ta.value();
System.out.println("数据库表名:"+value);
Field[] fs = c.getDeclaredFields();
for (Field f:fs) {
ColumnAnnotation ca = f.getAnnotation(ColumnAnnotation.class);
System.out.println(f.getName()+"属性,对应数据库中的字段:"+ca.columnName()+",数据类型"+ca.type()+",数据长度"+ca.length());
}
}
}
7.4内省
基于反射 , java所提供的一套应用到JavaBean的API
一个定义在包中的类,
- 拥有无参构造器
- 所有属性私有
- 所有属性提供get/set方法
- 实现了序列化接口
这种类, 我们称其为 bean类 .
Java提供了一套java.beans包的api , 对于反射的操作, 进行了封装
例子:
import java.io.Serializable;
public class Express implements Serializable {
private String number;
private String name;
private String phoneNumber;
private String address;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Express() {
}
@Override
public String toString() {
return "Express{" +
"number='" + number + '\'' +
", name='" + name + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
", address='" + address + '\'' +
'}';
}
}
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Neixing {
public static void main(String[] args) throws IntrospectionException {
Class c = Express.class;
BeanInfo bi = Introspector.getBeanInfo(c);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor pd:pds){
Method get = pd.getReadMethod();
Method set = pd.getWriteMethod();
System.out.println(get);
System.out.println(set);
System.out.println(pd.getName());
System.out.println(pd.getPropertyType());
}
}
}