(小白学JAVA之)Java高级特性知识点梳理

本文深入探讨Java集合框架的结构与泛型的应用,包括List、Set、Map接口及其常用实现类,如ArrayList、LinkedList、HashSet、HashMap的特性与使用方法。同时,介绍了泛型的概念、定义及在集合中的应用,提升代码的通用性和安全性。
摘要由CSDN通过智能技术生成

集合框架和泛型

用数组存储多个同类型的数据,会存在如下一些明显的缺陷:

  • 数组长度固定不变,不能很好地适应元素数量动态变化的情况
  • 可通过数组名.leng()获取数组的长度,却无法直接获取数组中实际存储的元素个数
  • 数组采用在内存中分配连续空间的存储方式存储,根据元素信息查找时效率比较低,需要多次比较

Java集合框架提供了一套性能优良、使用方便的接口和类,它们都位于java.util包中,其主要内容及彼此之间的关系如下图所示:
在这里插入图片描述
Java的集合类主要由Map接口和Collection接口派生而来,其中Collection接口有两个常用的子接口,即List接口和Set接口。

List接口

可以存储一组不唯一、无序的对象;List接口常用的实现类有ArrayList和LinkedList

ArrayList

ArrayList类底层为动态数组,遍历元素更快,改变值也就更快;它可以添加任何类型的数据,并且添加的数据都将转换成Object类型

ArrayList类的常用方法

在这里插入图片描述

具体实现步骤

1.导入ArrayList类
2.创建ArrayList对象,并添加数据
3.判断集合中是否包含某元素
4.移除索引为0的元素
5.把索引为1的元素替换为其他元素
6.输出某个元素所在的索引位置
7.清空ArrayList集合中的数据
8.判断ArrayList集合中是否包含数据

public static void main(String[] args){
	ArrayList list=new ArrayList(); // 1
	list.add("张三");
	list.add("李四");
	list.add("王五"); // 2
	//判断集合中是否包含"小刘" 3
	System.out.println(list.contains("小刘")); //输出false
	// 4
	list.remove(0);
	// 5
	list.set(1, "黄蓉");
	// 6
	System.out.println(list.indexOf("小龙女")) //没有该元素,输出-1
	// 7
	list.clear();
	// 8
	System.out.println(list.isEmpty()); //第7步已经清空,这里输出true
	//遍历
	for (int i=0; i<list.size(); i++){
		String name = (String)list.get(i);
		System.out.println(name);
	}
	for (Object obj:list){ //增强for
		String name = (String)obj;
		System.out.println(name);
	}
}

LinkedList

LinkedList类底层是双向链表结构,插入和删除更快。它支持实现所有List解耦可选的列表的操作,并允许元素值是任何数据,包括null

LinkedList类的常用方法

LinkedList除了包含ArrayList类所包含的方法外,还提供一些自身特有的方法
在这里插入图片描述

具体实现步骤

1.创建LinkedList对象,并添加数据
2.添加头条和末条元素
3.获取头条和末条元素
4.删除头条和末条元素

NewTitle car = new NewTitle(1,"汽车","管理员");
NewTitle medical = new NewTitle(2,"医学","管理员");
NewTitle fun = new NewTitle(3,"娱乐","管理员");
NewTitle gym = new NewTitle(4,"体育","管理员");

// 创建存储新闻标题的集合对象并添加数据
LinkedList newsTitleList = new LinkedList();
newsTitleList.add(car);
newsTitleList.add(medical);

// 2
newsTitleList.addFirst(fun);
newsTitleList.addLast(gym);

// 3
NewTitle first = (NewTitle) newsTitleList.getFirst();
NewTitle last = (NewTitle) newsTitleList.getLast();

// 4
newsTitleList.removeFirst();
newsTitleList.removeLast();

Set接口

Set接口可以存储一组唯一、无序的对象,它常用的实现类有HashSet

HashSet

HashSet集合的特点如下:

  • 集合内的元素是无序排列
  • HashSet类是非线程安全
  • 允许集合元素值为null
HashSet类的常用方法
方法作用
boolean add(Object o)如果Set中尚未包含指定元素o,则添加指定元素o
void clear()从Set中移除所有元素
int size返回Set中的元素的数量
boolean isEmpty()如果Set不包含任何元素,则返回true
boolean contains(Object o)如果Set包含指定元素o,则返回true
boolean remove(Object o)如果指定元素o存在于Set中,则将其移除

注意!Set接口不存在 get() 方法!

具体实现步骤

1.创建HashSet对象,并添加数据
2.获取新闻标题的总数
3.判断集合中是否包含汽车新闻标题
4.移除对象
5.判断集合是否为空
6.遍历集合

NewTitle car = new NewTitle(1,"汽车","管理员");
NewTitle medical = new NewTitle(2,"医学","管理员");
// 1
Set newsTitleList = new HashSet();
newsTitleList.add(car);
newsTitleList.add(medical);
// 2
newsTitleList.size();
// 3
newsTitleList.contains(car);
// 4
newsTitleList.remove(medical);
// 5
newsTitleList.isEmpty();
// 6
for (Object obj:newsTitleList){
	NewTitle title = (NewTitle) obj;
	System.out.println(title.getTitleName());
}
Iterator接口

Iterator接口表示对集合进行迭代的迭代器。Iterator接口为集合而生,专门实现集合的遍历。此接口主要有如下两个方法:

  • hasNext() 判断是否存在下一个可访问的元素,如果有元素可以迭代,则返回true
  • next() 返回要访问的下一个元素

使用Iterator接口遍历List类集合:(Set同理)

Iterator it = list.iterator();
while (it.hasNext()){
	System.out.println(it.next());
}

Map接口

Map接口存储一组成对的键(key)— 值(value)对象,提供key到value的映射,通过key来检索。Map接口中的key不要求有序,不允许重复。value同样不要求有序,但允许重复。

Map接口的常用用法

在这里插入图片描述

HashMap实现步骤

最常用的Map实现类是HashMap,其优点是查询指定元素效率高。

1.导入HashMap类
2.创建HashMap对象
3.调用HashMap对象的put()方法,向集合中添加数据
4.输出学员个数
5.输出键集
6.判断是否存在“Jack”这个键,如果存在,则根据键获取相应的值

// 1
Student student1 = new Student("李明","男");
Student student2 = new Student("刘丽","女");
// 2
Map students = new HashMap();
// 3
students.put("Jack", student1);
students.put("Rose", student2);
// 4
students.size();
// 5
students.keySet();
// 6
String key = "Jack";
if (students.containsKey(key)) {
	Student student = (Student) students.get(key);
	System.out.println("英文名为"+key+"的学员姓名:"+student.getName());
}

遍历HashMap集合

1.遍历:使用entrySet方法获取键值对的集合

Set set = students.entrySet();
Iterator itr = set.iterator();
while (itr.hasNext()){
	System.out.println(itr.next());
}

2.遍历键集:键集用Set存储

for (Object key:students.keySet()) {
	System.out.println(key.toString());
}

3.遍历值集:值集用Collection存储

Collection values = map.values();
for (Object value : values) {
	System.out.println(value);
}

Collections类

Collections类是Java提供的一个集合操作工具类,它包含了大量的静态方法,用于实现对集合元素的排序、查找和替换等操作。

注意!Collections和Collection是不同的,前者是集合的操作类,后者是集合接口。

Collections类常用方法

以下方法皆为静态方法:
sort() 排序
binarySearch() 查找
max()\min() 查找最大\最小值

Comparable接口

通过重写compareTo()方法,用来实现比较大小
定义语句:int compareTo(Object obj);
参数:obj即将要比较的对象
返回值:负整数、零或正整数,根据此对象是小于、等于还是大雨指定对象返回不同的值

实例:

public int compareTo(Object obj) {
	Student student = (Student) obj;
	//如果学号相同,那么两者就是相等的
	if (this.number==student.getNumber){
		return 0;
	//如果这个学生的学号大于传入学生的学号
	} else if (this.number>student.getNumber()){
		return 1;
	//如果这个学生的学号小于传入学生的学号
	} else {
		return -1;
	}
}

元素之间可以比较大小之后,就可以使用Collections类的sort()方法对元素进行排序操作了。Map接口本身是无序的,所以不能进行排序。可以对List接口进行排序,但注意必须是实现了Comparable接口的元素才可以。

ArrayList list = new ArrayList();
list.add(student1);
list.add(student2);
list.add(student3);
//sort()方法排序
Collections.sort(list);
//binarySearch()方法查找
int index = Collections.binarySearch(list, students3);
替换集合元素

如果需要把一个List集合中的所有元素都替换为相同的元素,则可以使用Collections类的静态方法fill()来实现

Collections.fill(list, "李明");

泛型

泛型的本质是参数化类型。Java语言引入泛型的好处是安全简单,且所有强制转换都是自动和隐式进行的,提高了代码的重用率

泛型的定义

语法格式:类1或者接口<类型实参>对象=new 类2<类型实参>();
例如:ArrayList<String> list = new ArrayList<String>();
上述代码表示创建了一个ArrayList集合,但规定该集合中存储的元素类型必须为String类型

泛型在集合中的应用

学习List接口时提到add()方法的参数是Object类型,无论什么对象放入List接口,或其子接口,或实现类,都会被转换为Object类型。在通过get()方法取出集合中的元素是必须进强制类型转换,不仅繁琐且容易出现异常。

引入泛型是如何解决上述问题的呢? 使用泛型集合在创建集合对象时指定了集合中元素的类型,从集合中取出元素时,无需进行强制类型转换,并且如果把非指定类型对象放入集合,会出现编译错误。

List和ArrayList的泛型形式是List<E> ArrayList<E>
Map和HashMap的泛型形式是Map<K,V> HashMap<K,V>

实用类

Java应用程序编程接口是运行库的集合,预定义了一些接口和类,程序员可以直接使用这些已经被打包的接口和类来开发具体的应用。

常用的包:
java.lang: 编写Java程序时最广泛使用的包,自动导入到所有的程序中,包含了Java程序的基础类和接口
java.util: 包含了系统辅助类,特别是Collection、List和Map等集合类
java.io: 包含了与输入\输出有关的类
java.sql: 包含了与数据库相关的类

枚举

枚举是指有一组固定的常量组成的类型,使用关键字enum定义

[Modifier] enum enumName {
	enumContantName1, enumConstantName2... // 表示枚举常量列表,枚举常量之间以逗号隔开
	[field, method] //表示其他的成员,包括构造方法,置于枚举常量的后面
}
//在枚举中,如果除了定义枚举常量,还定义了其他成员,则枚举常量列表必须以分号(;)结尾

实例:

public enum Week{
	MON, TUE, WED, THU, FRI, SAT, SUN
}
public void doWhat(Week day){
	switch(day){
		case MON:
		case TUE:
		case WED:
		case THU:
		case FRI:
			System.out.println("工作日");
			break;
		case SAT:
		case SUN:
			System.out.println("周末");
			break;
	}
}

包装类

Java语言是面向对象的,但是基本数据类型不是面向对象的。包装类的用途主要有两个:

  • 包装类作为和基本数据类型对应的类存在,方便对象操作
  • 包装类包含每种基本数据类型的相关属性,如最大最小值,以及祥光的操作方法
基本数据类型包装类
byteByte
booleanBoolean
shortShort
cahrCharacter
intInteger
longLong
floatFloat
doubleDouble

拆箱和装箱

装箱:把基本数据类型变为包装类型
拆箱:把包装类型转为基本数据类型

赋值方式

以Integer为例:几种赋值方法

  • new Integer(整形)
  • new Integer(字符串)
  • Interger.valueOf(字符串/整形)
  • Integer.paraseInt(字符串)

注意:Character类的valueOf()方法只有一个版本的定义,即valueOf(char c),它返回一个表示指定char值的Character对象

Math类

java.lang.Math类提供了常用的数学运算方法和两个静态常量E(自然对数的底数) 和PI(圆周率)
这个类是final类,因此没有子类,Math类常见方法:

  • static double abs(double a) 返回一个绝对值
  • static double max(double a, double b) 返回其中一个较大的值
  • static double random() 返回一个随机值

Random类

Random类用于生成随机数

构造方法说明
Random()创建一个新的随机数生成器
Random(long seed)使用单个long种子创建一个新的随机数生成器

用同一个种子值来初始化两个Random 对象,然后用每个对象调用相同的方法,得到的随机数也是相同的

比较常用的是nextInt()方法,它返回下一个伪随机整型数

int nextInt();
int nextInt(int n); //从0到n之间(不包括n)

日期操作类

Date类 对象用来表示日期和时间,该类提供了一系列操作日期和时间各组成部分的方法

Calendar类 也是用来操作日期和时间的类,它是抽象类,可以通过静态方法getInstance()获得Calender类的对象。它的方法如下:

方法说明
int get(int field)返回给定日历字段的值
YEAR指示年
MONTH指示月
DAY_OF_MONTH指示一个月中的某天
DAY_OF_WEEK指示一个星期中的某天

DateFormat类 是一个抽象类,提供了多种格式化和解析时间的方法。使用比较多的是它的子类SimpleDateFormat

Date date = new Date();
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("当前时间为"+formater.format(date));

String类

String类的常用方法

1.求字符串长度 str.length()

2.字符串比较 字符串1.equals(字符串2)
需要注意的是:“==”比较的是两个字符串对象在内存中的地址,而equals()比较的是两个字符串对象的值
忽略大小的字符串比较字符串1.equalsIgnoreCase(字符串2)
转大小写 toLowerCase() / toUpperCase()

3.字符串拼接 字符串1.concat(字符串2)

4.字符串提取和查询
在这里插入图片描述
5.字符串拆分 字符串名.split(separator, limit);
separator和limit均为可选项

StringBuffer和StringBuilder类

1.toString()方法
2.append()方法
3.inset()方法 字符串.insert(位置,参数)
4.replace()方法字符串名.replace(int start,int end,String str)

三者比较

  • String:不可被改变,真正意义上的安全,在频繁字符串拼接的情况下,速度非常慢
  • StringBuffer:线程安全,速度慢
  • StringBuilder:线程不安全,速度快

I/O

java.io包提供了一些接口和类,对文件进行基本的操作,包括对文件和目录属性的操作、对文件读写的操作等

File类访问文件属性

在这里插入图片描述

File类的常用方法

在这里插入图片描述

流是指一连串流动的字符,是以先进先出的方式发送和接受数据的通道
在这里插入图片描述

流的分类

在这里插入图片描述

InputStream
方法说明
int read()从输入流中读取下一个字节数据
int read(byte[] b)从输入流中读取数据,并将数据存储在缓冲区数组b中,返回实际读取的字节数
int read(byte[] b, int off, int len)从输入流中读取最多len长度的字节,保存到字节数组b中,保存的位置从off开始
void close()关闭输入流

InputStream类的常用子类有FileInputStream,用于从文件中读取数据

FileInputStream读文件的流程:
1、FileInputStream对象和String对象声明
2、创建FileInputStream对象(文件路径或File对象)
3、读单字节或整个读到byte数组中
4、转成字符串
5、关闭FileInputStream流
6、返回结果字符串

public static String readFile(String path){
	FileInputStream fis = null;
    String str = null;
    try {
        fis = new FileInputStream(path);
        byte[] b = new byte[fis.available()];
        fis.read(b);
        str = new String(b);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return str;
}
OutputStream
方法说明
void write(int c)将制定的字节数据写入此输出流中
void write(byte[] buf)将数组buf中的所有字节写入此输出流中
void write(byte[] b, int off, int len)将字节数组中从偏移量off开始的长度为len的字节数据输出到输出流中
void close()关闭输出流

OutputStream类的常用子类为FileOutputStream,用于向文件写数据

FileOutputStream写文件的流程:
1、File对象装载文件路径
2、判断文件父级目录是否存在,不存在则创建
3、声明FileOutputStream对象
4、创建FileOutputStream对象(file对象,是否追加)
5、把要写的字符串转成byte数组,并写入输出流
6、关闭FileOutputStream流

public static void writeFile(String str, String path, boolean isAppend){
	File f =new File(path);
    if (!f.getParentFile().exists()){
        f.getParentFile().mkdirs();
    }
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(f, isAppend);
        byte[] b = str.getBytes();
        fos.write(b);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Reader
方法说明
int read()从输入流中读取单个字符,返回所读取的字符数据
int read(byte[] c)从输入流中最多读取c.length个字符
int read(char[] c, int off, int len)从输入流中读取最多len个字符
void close()关闭流

Reader类的常用子类为BufferedReader,接受Reader对象作为参数,并对其添加字符缓冲器

使用BufferedReader类和FileReader类读取文本文件数据:

public static String readBuffer(String path){
    File f = new File(path);
    FileReader fr = null;
    BufferedReader br = null;
    String str = null;
    try {
        fr = new FileReader(f);
        br = new BufferedReader(fr);
        String s;
        StringBuffer sb = new StringBuffer();
        while ((s=br.readLine())!=null){
            sb.append(s);
        }
        str = sb.toString();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return str;
}
Writer
方法说明
void write(String str)将str字符串里包含的字符输出到指定的输出流中
void write(String str, int off, int len)将str字符串里从off位置开始,长度为len的多个字符输出到输出流中
void close()关闭输出流
void flush()刷新输出流

Writer类的常用子类为BufferedWriter,用于将数据缓冲到字符输出流

使用BufferedWriter以及FileWirter对象向文本文件中写数据

public static void writeBuffer(String str, String path, boolean isAppend){
	FileWriter fw = null;
	BufferedWriter bw = null;
	try {
		fw = new FileWriter(path, isAppend);
		bw = new BufferedWriter(fw);
		bw.write(str);
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

注意! 在操作上字节流与字符流有一个区别,字符流在操作时使用了缓冲区(内部存储器),而字节流在操作时直接操作文件,不会使用缓冲区;所有的这些方法在出现错误时都会抛出IOException异常。

读写二进制文件

读写二进制文件(例如图片)文件常用的类有DataInputStream和DataOutputStream

实例:

public static void copyData(String fromPath, String targetPath){
    FileInputStream fis = null;
    DataInputStream dis = null;
    FileOutputStream fos = null;
    DataOutputStream dos = null;
    try {
        fis = new FileInputStream(fromPath);
        dis = new DataInputStream(fis);
        fos = new FileOutputStream(targetPath);
        dos = new DataOutputStream(fos);
        int tmp;
        while ((tmp=dis.read())!=-1){
            dos.write(tmp);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            dos.close();
            fos.close();
            dis.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象流

序列化和反序列化:

  • 场景一:内存对象需要在其它环境下使用
    两个进程间进行网络通信时,无论是发送何种类型的数据,均需以二进制序列形式进行传送
    发送方必须将数据对象(比如Java对象)转化为字节序列
    接收方则需要将接收到的字节序列再还原成Java对象
  • 场景二:内存对象需要在将来某个时间使用
    将内存中的数据对象永久存储在磁盘中(持久化)

常用序列化方案:
在这里插入图片描述

序列化保存对象信息

步骤可以概括成如下两大步:
1.创建一个对象输出流(ObjectOutputStream),它可以包装一个其他类型的输出流,如文件输出流FileOutputStream
2.通过对象输出流的writeObject()方法写对象,也就是输出可序列化对象

实例:使用序列化将学生对象保存到文件中
1.引入相关类
2.创建学生类,实现Serializable接口
3.创建对象输出流
4.调用writeObject()方法将对象写入文件
5.关闭对象输出流

public class Student implements Serializable{
	//Student属性和方法
}

public class TestStudent{
	public static void main(String[] args){
		ObjectOutputStream oos = null;
		try{
			//创建ObjectOutputStream输出流
			oos=new ObjectOutputStream(new FileOutputStream("目标文件路径"));
			Student stu = new Student("李梅", 22, "女");
			//对象序列化,写入输出流
			oos.writeObject(stu);
		} catch(IOException e) {
			e.printStackTrace();
		} finally {
			if (oos!=null) {
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

//oos还可以写入集合中的对象
ArrayList<Student> list = new ArrayList<Student>();
list.add(stu);
list.add(stu1);
oos.writeObject(list);

反序列化获取对象信息

反序列化的步骤大致概括为以下两步:
1.创建一个对象输入流(ObjectInputStream),它可以包装一个其他类型的输入流,如文件输入流FileInputStream
2.通过对象输入流的readObject()方法读取对象,该方法返回一个Object类型的对象,如果程序知道该Java对象的类型,则可以将该对象强制转换成其真实的类型。

实例:使用反序列化读取文件中的学生对象
1.引入相关类
2.创建对象输入流
3.调用readObject()方法读取对象
4.关闭对象输入流

ObjectInputStream ois - null;
trry {
	ois = new ObjectInputStream(new FileInputStream("读取文件路径"));
	Student stu = (Student) ois.readObject();
	//输出生成后的对象信息
	System.out.println("姓名为"+stu.getName());
	...
} catach (IOException e) {
	e.printStackTrace();
} finally {
	if (ois!=null) {
		try {
			ois.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

//集合的反序列化
ArrayList<Student> List = (ArrayList<Student>) ois.readObject();
Student stu = (Student) ois.readObject();
for (Student stu:list) {
	System.out.println("姓名为"+stu.getName());
}

反射

Java的反射机制是Java特性之一;Java反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能

反射的作用:

  • 在运行时获取类的修饰符,包名,类名,实现的接口,继承的父类
  • 在运行时获取类的所有属性名,修饰符,属性类型
  • 在运行时获取所有方法,方法的返回值类型,方法名,方法参数数量,方法参数类型
  • 在运行时调用加载类的方法

访问类包含的构造

public class TestConstructor {
    public static void main(String[] args) throws Exception{
        Class<Student> c = Student.class;
        Constructor<Student> sClass = c.getDeclaredConstructor(int.class, String.class, String.class);
        sClass.setAccessible(true);
        Student s = sClass.newInstance(2, "baba", "male");
        System.out.println(s);
    }
}

访问类包含的方法

public class TestMethod {
    public static void main(String[] args) throws Exception {
        Student s = TestStudent123.getStudent();
        Class<Student> c = Student.class;
        Method setStuId = c.getDeclaredMethod("setStuId", int.class);
        setStuId.setAccessible(true);
        
        setStuId.invoke(s, 3);
        Method getStuId = c.getDeclaredMethod("getStuId");
        getStuId.setAccessible(true);
        Object stuId = getStuId.invoke(s);
        
        System.out.println(s);
        System.out.println(stuId);
    }
}

访问类包含的属性

public class TestField {
    public static void main(String[] args) throws Exception{
        Student student = TestStudent123.getStudent();
        Class<Student> c = Student.class;
        Field stuId = c.getField("stuId");
        stuId.set(student, 11);
        Field stuName = c.getDeclaredField("stuName");
        stuName.setAccessible(true);
        stuName.set(student, "张三");
        
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            System.out.println(field.get(student));
        }
        System.out.println(student);
    }
}

多线程

进程

进程是程序的一次动态执行过程,它有如下特点:

  • 进程是系统运行程序的基本单位
  • 每一个进程都有自己独立的一块内存空间、一组系统资源
  • 每一个进程的内部数据和状态都是完全独立的

线程

线程是进程中执行运算的最小单位,一个进程在其执行过程中可以产生多个线程,而线程必须在某个进程内执行
线程和进程既有联系又有区别:

  • 一个进程中至少要有一个线程
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源
  • 处理机分配给线程,即真正在处理机上运行的是线程

多线程的优势

多线程程序可以带来更好的用户体验,避免因程序执行过慢而导致出现计算机死机或者白屏的情况
多线程程序可以最大限度地提高计算机系统的利用效率

编写线程类

使用一个线程的过程可以分为如下4个步骤:
在这里插入图片描述

使用Thread类

定义MyThread类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

public class MyThread extends Thread{
	private int count=0;
	//重写run方法
	public void run(){
		while(count<100){
			count++;
			System.out.println(count)
		}
	}
}
publict class Test{
	public static void main(String[] args){
		MyThread mt = new MyThread();
		mt.start();
	}
}

使用Runnable接口

定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

public class MyThread implements Runnable{
	private int count = 0;
	public void run(){
		while(count<100){
			count++;
		}
	}
}
public class Test{
	public static void main(Stirng[] args){
		Thread thread = new Thread(new MyThread());
		thread.start();
	}
}

比较两种创建线程的方式

  • 继承Thread类:
    编写简单,可直接操作线程
    适用于单继承
  • 实现Runnable接口:
    避免单继承局限性
    便于共享资源

线程的状态

在这里插入图片描述

线程调度

在这里插入图片描述
实现线程调度的方法:

  • join()方法
for(int i=0;i<10;i++){
	if(i==5){
		MyThread mt = new MyThread('MyThread');
		try{
			mt.start();
			mt.join();
		} catch...
	}
}
  • sleep方法
try{
	Thread.sleep(100) //睡眠100毫秒
} catch...
  • yield方法
for(int i=0;i<5;i++){
	System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
	if(i==3){
		System.out.print("线程礼让:");
		Thread.yield();	
	}
}

线程同步

多个线程操作同一共享资源时,将引发数据不安全问题

实现线程同步

1.同步方法
通过在方法声明中加入synchronized关键字来声明同步方法

访问修饰符 synchronized 返回类型 方法名{}
//或者
synchronized 访问修饰符 返回类型 方法名{}

2.同步代码块
同步代码块的语法格式如下:

synchronized(synObjcet){ //通常填写this
	//需要同步访问控制的代码
}

线程通信

Java提供了如下三个方法实现线程之间的通信:

  • wait():调用wait()方法会挂起当前线程,并释放共享资源的锁
  • notify():调用任意对象的notify()方法会在因调用该对象的wait()方法而阻塞的线程中随机选择一个线程解除阻塞,但要等到获得锁之后才可以真正执行
  • notifyall():调用了notifyall()方法会将因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞

注意: 这三个方法被所有的类继承并且不允许重写,只能在同步方法或同步代码块中使用。

XML

XML简介

  • XML(EXtensible Markup Language),可扩展标记语言
  • 特点:
    XML与操作系统、编程语言的开发平台无关
    实现不同系统之间的数据交换
  • 作用:
    数据交互
    配置应用程序和网站
    Ajax基石

XML文本结构

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <!--图书信息 -->
    <book id="bk101">
        <author>王珊</author>
        <title>.NET高级编程</title>
        <description>包含C#框架和网络编程等</description>
    </book>
    <book id="bk102">
        <author>李明明</author>
        <title>XML基础编程</title>
        <description>包含XML基础概念和基本作用</description>
    </book>
</books>

XML声明

<?xml version="1.0" encoding="UTF-8"?>

XML声明由以下几个部分组成:
version:文档符合XML1.0规范
encoding:文档字符编码,默认为“UTF-8”

XML标签

在XML中用<>括起来的各种标签来标记数据,如<author></author>

根元素

每个XML文档必须有且仅有一个根元素,如<book></book>
根元素的特点如下:
根元素是一个完全包括文档中其他所有元素的元素
根元素的起始标签要放在所有其他元素的起始标签之前
根元素的结束标签要放在所有其他元素的结束标签之后

元素与属性

XML文档内容由一系列标签元素组成

<元素名 属性名=“属性值”>元素内容</元素名>
编写注意事项
  • 所有XML元素都必须有结束标签
  • XML标签对大小写敏感
  • XML必须正确的嵌套
  • 同级标签以缩进对齐
  • 元素名称可以包含字母、数字或其他的字符
  • 元素名称不能以数字或者标点符号开始
  • 元素名称中不能含空格

注意!!
属性值用双引号包裹
一个元素可以有多个属性
属性值中不能直接包含<、“、&
不建议使用的字符:‘、>

命名空间

命名空间的必要性: XML解析器在解析XML文档时,对于重名元素,可能会出现解析冲突。命名空间有助于标准化元素和属性,并为它们加上唯一的标识

声明命名空间:

xmlns:[prefix]="[命名空间的URL]"

属性和命名空间: 除非带有前缀,否则属性属于它们的元素所在的命名空间

实例应用

<?xml version="1.0" encoding="UTF-8"?>
<cameras xmlns:canon="http://www.canon"
    xmlns:nikon="http://www.nikon.com">
    <canon:camera prodID="P663" name="Camera傻瓜相机"/>
    <nikon:camera prodID=“K29B3” name=“Camera超级35毫米相机"/>
</cameras>

解析XML技术

  • DOM
    基于XML文档树结构的解析
    适用于多次访问的XML文档
    特点:比较消耗资源
  • SAX
    基于事件的解析
    适用于大数据量的XML文档
    特点:占用资源少,内存消耗小
  • DOM4J
    非常优秀的Java XML API
    性能优异、功能强大
    开放源代码

这里着重讲述使用DOM读取XML数据

DOM

DOM介绍:文档对象模型(Document Object Model)
DOM把XML文档映射成一个倒挂的树
在这里插入图片描述

访问DOM树节点

在这里插入图片描述

public class TestXML {
    Document document;
    public void setDocument(String xmlPath){
        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder=factory.newDocumentBuilder();
            document=builder.parse(xmlPath);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception{
        TestXML xml = new TestXML();
        xml.setDocument("D:\\KB09\\JAVA\\2020.8.7\\src\\Demo\\phone_info.xml");
        NodeList brands = xml.document.getElementsByTagName("brand");
        Node item = brands.item(0);

        Element e = (Element) item;
        System.out.println(e.getAttribute("name"));
//        for (int i = 0; i < brands.getLength(); i++) {
//            Node n = brands.item(i);
//            if (n instanceof Element){
//                Element e = (Element) n;
//                System.out.println(e.getAttribute("name"));
//            }
//        }
        NodeList hwTypes = e.getChildNodes();
        Node root = xml.document.getElementsByTagName("phoneInfo").item(0);
        Element sx = xml.document.createElement("brand");
        sx.setAttribute("name","三星");
        Element sxType = xml.document.createElement("type");
        sxType.setAttribute("name", "note20");
        sx.appendChild(sxType);
        root.appendChild(sx);


        //保存更改
        TransformerFactory tf = TransformerFactory.newInstance();
        tf.setAttribute("indent-number", 4);
        Transformer t = tf.newTransformer();
        t.setOutputProperty(OutputKeys.INDENT,"yes");
        t.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
        DOMSource source = new DOMSource(xml.document);
        FileOutputStream fos = new FileOutputStream("D:\\KB09\\JAVA\\2020.8.7\\src\\Demo\\phone_info.xml");
        StreamResult sr = new StreamResult(new OutputStreamWriter(fos));
        t.transform(source, sr);

//        for (int i = 0; i < hwTypes.getLength(); i++) {
//            Node n = hwTypes.item(i);
//            if (n instanceof Element){
//                Element type = (Element) n;
//                Node firstChild = type.getFirstChild();
//                if (firstChild instanceof Text){
//                    Text t=(Text) firstChild;
//                    System.out.println(t.getWholeText().trim());
//                }
//            }
//        }
//
//        Node item2 =brands.item(1);
//        Element e2 = (Element) item2;
//        System.out.println(e2.getAttribute("name"));
//        NodeList appleTypes = e2.getChildNodes();
//        for (int i = 0; i < appleTypes.getLength(); i++) {
//            Node n = appleTypes.item(i);
//            if (n instanceof Element){
//                Element type = (Element) n;
//                Node firstChild = type.getFirstChild();
//                if (firstChild instanceof Text){
//                    Text t=(Text) firstChild;
//                    System.out.println(t.getWholeText().trim());
//                }
//            }
//        }


//        for (int i = 0; i < brands.getLength(); i++) {
//            Node item = brands.item(i);
//            Element e = (Element) item;
//            System.out.println(e.getAttribute("name"));
//            NodeList Types = e.getChildNodes();
//            for (int j = 0; j < Types.getLength(); j++) {
//                Node node = Types.item(j);
//                if (node instanceof Element){
//                    Element type = (Element) node;
//                    Node firstChild = type.getFirstChild();
//                    if (firstChild instanceof Text){
//                        Text t = (Text) firstChild;
//                        System.out.println(t.getWholeText().trim());
//                    }
//                }
//            }
//            System.out.println();
//        }

    }
}

JSON

JSON简介

  • JSON(JavaScript Object Notation)是JavaScript中的对象表示法
  • 轻量级的文本数据交换格式,独立于JavaScript语言
  • 具有自我描述性
  • 比XML传输速度快

JSON语法规则

  • 数据由名称/值对构成
  • 数据之间由逗号分隔
  • 大括号内为对象
  • 中括号内为数组

Java对象转为JSON字符串

使用FastJason(下载地址:FastJason

class Student{
    private String name="";
    private int age;
    private List<String> skills;

    public Student(String name, int age, List<String> skills) {
        this.name = name;
        this.age = age;
        this.skills = skills;
    }
......//省略getter和setter
}

Student stu=new Student("Jason",20, Arrays.asList("Java", "Hadoop", "Python"));
String stuJson=com.alibaba.fastjson.JSON.toJSON(stu).toString();
System.out.println(stuJson);

JSON字符串转为Java对象

String json="{\"skills\":[\"Java\",\"Hadoop\",\"Python\"],
			\"name\":\"Jason\",
			\"age\":20
			}";
Student stuNew = com.alibaba.fastjson.JSON.parseObject(json,Student.class);
System.out.println(stuNew.getName());

正则表达式

正则表达式简介:

  • 正则表达式描述了一种字符串匹配的模式,也称规则表达式
  • 常用于检索、替换符合指定模式(规则)的文本
  • 大多数语言都支持正则表达式

正则表达式语法

  • 正则表达式是由普通字符与特殊字符组成的字符串

  • 普通字符
    原义字符、非打印字符

  • 特殊字符
    元字符:* + ? $ ^ . | \ ( ) { } [ ]
    在这里插入图片描述

  • 非打印字符
    在这里插入图片描述

  • 预定义字符
    在这里插入图片描述

JAVA正则表达式

java.util.regex包
Pattern类:表示一个正则表达式,或者说匹配模式
Matcher类:Pattern对象matcher()方法的返回值,表示正则表达式对输入字符串的匹配结果

分组

如何获取字符串“hello”中的字符“e”?

Pattern p=Pattern.compile("h(\\w*)llo");
Matcher matcher=p.matcher("hello");
if(matcher.matches())
	System.out.println(matcher.group(1));//输出匹配结果

命名分组

如何获取字符串“hello”中的字符“e”?

Pattern p=Pattern.compile("h(?<result>\\w*)llo");
Matcher matcher=p.matcher("hello");
if(matcher.matches())
	System.out.println(matcher.group("result"));//输出匹配结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值