核 心 类 库

文章目录

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

区 别SerializableExternalizable
实现复杂度实现简单,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缓存线程池

缓存线程池(长度无限制)
执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在,则创建线程 并放入线程池, 然后使用
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定长线程池

定长线程池(长度是指定的数值)
执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
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 效果一致.
单线程线程池
执行流程:

  1. 判断线程池 的那个线程 是否空闲
  2. 空闲则使用
  3. 不空闲,则等待 池中的单个线程空闲后 使用
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周期性任务定长线程池

周期任务 定长线程池.
执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  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)。
特性:

  1. xml具有平台无关性(Windows/Linux都可以), 是一门独立的标记语言(Java/C都可以解析).
  2. xml具有自我描述性

作用:

  1. 网络数据传输
  2. 数据存储
  3. 配置文件

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

通过路径快速的查找一个或一组元素
路径表达式:

  1. / : 从根节点开始查找
  2. // : 从发起查找的节点位置 查找后代节点 ***
  3. . : 查找当前节点
  4. . . : 查找父节点
  5. @ : 选择属性. *
    属性使用方式:
    [@属性名=‘值’]
    [@属性名>‘值’]
    [@属性名<‘值’]
    [@属性名!=‘值’]
    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生成

步骤:

  1. 通过文档帮助器 (DocumentHelper) , 创建空的文档对象
    Document doc = DocumentHelper.createDocument();
  2. 通过文档对象, 向其中添加根节点
    Element root = doc.addElement("根节点名称");
  3. 通过根节点对象root , 丰富我们的子节点
    Element e = root.addElement("元素名称");
  4. 创建一个文件输出流 ,用于存储XML文件
    FileOutputStream fos = new FileOutputStream("要存储的位置");
  5. 将文件输出流, 转换为XML文档输出流
    XMLWriter xw = new XMLWriter(fos);
  6. 写出文档xw.write(doc);
  7. 释放资源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

三种使用方式:

  1. @SuppressWarnings(“unchecked”) [^ 抑制单类型的警告]
  2. @SuppressWarnings(“unchecked”,“rawtypes”) [^ 抑制多类型的警告]
  3. @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 - 标记这个注解是自动继承的

  1. 子类会继承父类使用的注解中被@Inherited修饰的注解
  2. 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有 被@Inherited修饰
  3. 类实现接口时不会继承任何接口中定义的注解

7.2.4自定义注解

定义格式:@interface自定义注解名{}
注意事项

  1. 定义的注解,自动继承了java.lang,annotation.Annotation接口
  2. 注解中的每一个方法,实际是声明的注解配置参数
    方法的名称就是 配置参数的名称
    方法的返回值类型,就是配置参数的类型。只能是:基本类型/Class/String/enum
  3. 可以通过default来声明参数的默认值
  4. 如果只有一个参数成员,一般参数名为value
  5. 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、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的几种方式:

  1. 如果在编写代码时, 指导类的名称, 且类已经存在, 可以通过
    包名.类名.class 得到一个类的 类对象
  2. 如果拥有类的对象, 可以通过
    Class 对象.getClass() 得到一个类的类对象
  3. 如果在编写代码时, 知道类的名称 , 可以通过
    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反射中的构造方法

  1. 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组)
    例如:构造方法如下: Person(String name,int age)
    得到这个构造方法的代码如下: Constructor c = p.getClass().getConstructor(String.class,int.class);
  2. 获取构造方法数组 getConstructors();
  3. 获取所有权限的单个构造方法 getDeclaredConstructor(参数类型的class对象数组)
  4. 获取所有权限的构造方法数组 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反射中的方法

  1. getMethod(String methodName , class… clss)
    根据参数列表的类型和方法名, 得到一个方法(public修饰的)
  2. getMethods();
    得到一个类的所有方法 (public修饰的)
  3. getDeclaredMethod(String methodName , class… clss)
    根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
  4. 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反射中的属性

  1. getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
  2. getDeclaredFields() 获取所有属性
  3. getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
  4. 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());
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值