基于Java的面向对象设计实践

面向对象设计实践(基于java)

caiyi 2021/10/1

source:https://www.icourse163.org/spoc/course/ECNU-1464731161

期末划考试范围:junit单元测试,第三章文本处理,第四章文件处理,第五章多线程并行(5.4 5.7 5.8很少考 5.9定时任务要考),第六章网络编程(6.6 6.7 6.8不考),第七章数据库编程(7.3不考),第八章(8.1-8.4考的少 只考8.5 8.6)

第一章 Maven

Maven概念

自动下载和管理jar包,配置build path,存在本地进行管理,编译、测试、运行、和打包发布java项目的构建工具

Maven编译工作流程

image-20211001132250430

Maven目录结构

image-20211001132453129

Maven项目构建流程

首先创建maven项目

image-20211001125648454

选择 maven project

image-20211001125742779

勾选 create a simple project

image-20211001125829422

输入 group id (组织名)、artifact id(作品名/项目名)

image-20211001125933863

然后在mvn中央仓库搜索工具包的名字

https://mvnrepository.com/

image-20211001130323919

选择合适的版本,复制依赖文本

image-20211001130449796

将依赖文本添加到项目pom.xml中

image-20211001130612178

Maven编译和运行

右键项目 → Run As → Maven Build

image-20211001130642217

在Goals输入 clean package → Apply → Run

image-20211001130808053

编译成功

编译过程中他会自动把jar包下载下来

image-20211001130907954

运行程序

image-20211001131201549

image-20211001131339068

总结,构建工具的功能

  • 自动帮程序员甄别和下载第三方库(jar)

  • 完成整个项目编译(调用javac.exe)

  • 完成整个项目单元测试流程(调用JUnit工具)

  • 完成项目打包(jar/war等格式,调用jar.exe)

一些常用的依赖文本

opencc4j 简体转繁体

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>opencc4j</artifactId>
    <version>1.7.1</version>
</dependency>

Junit 测试

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    <scope>test</scope>
</dependency>

commons math 数学

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

pinyin4j 汉字转拼音

<dependency>
    <groupId>com.belerweb</groupId>
    <artifactId>pinyin4j</artifactId>
    <version>2.5.1</version>
</dependency> 

Maven报错解决办法

image-20211001141811008

在pom.xml中添加以下配置语句

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

第二章 单元测试和Junit

软件测试

在规定的条件下对程序进行操作, 以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程

软件测试分类

单元 vs 集成测试

白盒 vs 黑盒测试

自动 vs 手动测试

回归测试、压力测试

JUnit:一个java语言的单元测试框架,大部分java IDE都集成了JUnit作为单元测试工具

JUnit 用法

用上一章的方法新建一个maven项目

image-20211001135252758

⭐️记得在 pom.xml 里导入 JUnit 的依赖文本,否则会报错

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
    </dependency>
</dependencies>

观察项目目录,我们在src/main/java 里面放业务代码,src/test/java 里面放测试代码

image-20211001140941537

在 src/main/java 新建 LeapYear.java,给定一个年份,判断是不是闰年

public class LeapYear {
    
	public boolean isLeapYear(int year) {
		if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0){
			return true;
		}
		else{
			return false;
		}
	}
}

在 src/test/java 新建 LeapYearTest.java

每一个测试方法的头部加@Test注解,这样JUnit会自动执行这些测试方法

import是导入一个类或者几个类,import static是导入某一个类的所有静态方法,以便我们在程序调用的时候不需要再写 Assert.assertEquals()

import static org.junit.Assert.*;
import org.junit.Test;

public class LeapYearTest {
	
	@Test
	public void test() {
		assertEquals(true,new LeapYear().isLeapYear(2020));
		assertEquals(false,new LeapYear().isLeapYear(2019));
	}
}

junit单元测试,右键LeapYearTest.java → Run As → JUnit Test

image-20211001140535744

结果正确!

image-20211001140700647

修改一下代码,运行结果错误!和我们期待的值不一样

image-20211001140747352

我们也可以通过Maven Test来运行JUnit程序

image-20211001141241534

运行错误

image-20211001142551191

修改第八行的 false 为 true,运行正确

image-20211001142711084

二者区别

JUnit一次只能执行一个test类,Maven可以批量执行

第三章 高级文本处理

3.1 Java字符编码

ASCII码

用一个字节(1 Byte = 8 bits)来存储 a-z A-Z 0-9和一些常用的符号,回车13,零48,A65,a97

Unicode编码

编码方案有UTF-8,兼容ASCII,变长1-4个字节存储字符,经济方便传输

Java的字符编码

源文件编码:采用UTF-8编码

程序内部采用UTF-16编码存储所有字符(不是程序员控制)

和外界(文本文件)的输入输出尽量采用UTF-8编码

通过几个程序了解一下Java字符编码

获取默认字符集,本机默认是GBK

输出所有支持的字符集

image-20211001145540616

用UTF-8格式写入文件

image-20211001150526788

写入成功

image-20211001150605400

接着用UTF-8编码读取文件

image-20211001150824610

读取成功!

image-20211001150845142

再试试用 GBK 编码读取文件呢,可以发现它乱码了

image-20211001151020929

3.2 Java国际化编程

多语言版本的软件,一套语言多个语言包,根据语言设定,可以切换显示文本

主要用到两个类

java.util.ResourceBundle 用于加载一个语言_国家语言包
java.util.Locale 定义一个语言_国家

Locale类

属性:语言(zh, en),国家地区(CN, US)

方法:getAvailableLocales(), getDefault()返回默认的

ResourceBundle类

→ 根据 Locale 的值加载对应的properties文件

→ 存储语言集合中所有的K-V对

→ getString(String key) 返回所对应的value

properties语言文件

我们所有的文本值都放在 properties文件里面,存放的都是KV对,存储文件必须是ASCII编码或Unicode编码

命名规则:包名 _ 语言 _ 国家地区.properties、

可以采用native2ascii.exe进行转码

image-20211001152828691

国际化转化演示

image-20211001162745441

所有的文本值都放在 properties文件里面

image-20211001173235029

image-20211001173250713

其他国际化

日期时间国际化 DateTimeFormatter和Locale的结合

数字金额国际化 NumberFormat和Locale的结合

解决引入java.util 爆红的方法

右键项目 → Build Path → Configure Build Path

image-20211001155244228

Java Build Path → Libraries选项卡 → 右侧Add Library按钮 → JRE System Library

image-20211001161001548

3.3 高级字符串处理

3.3.1 正则表达式

用事先定义好的一些特定字符、及这些特定字符的组合,组成一 个“规则字符串”,用于测试字符串内的模式、识别和替换文本、提取文本

匹配模板:定界符、原子、特殊功能字符、模式修正符

Java.util.regex包

  • Pattern 正则表达式的编译表示

    compile 编译一个正则表达式为Pattern对象

    matcher 用Pattern对象匹配一个字符串,返回匹配结果

  • Matcher

    Index Methods(位置方法) // start(), start(int group), end(), end(int group)

    Study Methods(查找方法) // lookingAt(), find(), find(int start), matches()

    Replacement Methods(替换方法) //replaceAll(String replacement)

例子

example1:一个pattern就代表一个正则表达式

image-20211001171430125

image-20211001171520875

example2:lookingAt()和matches()的区别

image-20211001171647436

example3:替换掉满足匹配模式的字符串为另一个替换字符文额 e

额

example4:把所有dog替换为cat,dogs替换为cats

image-20211001172519241

example5:用replaceAll()的方法修改example3

image-20211001172744500

example6:邮箱的正则表达式判断

image-20211001172957076

public static  void regularExpression() {
    String REGEX_EMAIL = "^\\w+((-\\w+)|(\\.\\w+))*@[A-Za-z0-9]+(([.\\-])[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$";//email
    Pattern pattern = Pattern.compile(REGEX_EMAIL);

    String[] emails = new String[]{"123^@qq.com", "name_321@163.com", "+whatever*72@gmail.com"};
    for(String email : emails) {
        Matcher matcher = pattern.matcher(email);
        if (matcher.matches()) {
            System.out.println(email + " is valid email.");
        } else {
            System.out.println(email + " is not valid email.");
        }
    }
}

3.3.2 其他字符串处理

集合和字符串互转

引入依赖文件

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

import依赖包

import org.apache.commons.lang3.StringUtils;

public class String2List {

	public static void main(String[] args) {
		List<String> names = new LinkedList<String>();
        names.add("Xiaohong");
        names.add("Xiaoming");
        names.add("Daming");
        names.add("Xiaohei");
        
        //从ArrayList变到字符串
        String str1 = String.join(",", names);  //String.join, JDK 8 引入
        System.out.println(str1);
       
        String str2 = StringUtils.join(names, ",");  //Apache Commons Lang
        System.out.println(str2);
        
        //从字符串变回ArrayList
        List<String> names2 = Arrays.asList(str2.split(",")); 
        for(String name:names2)
        {
        	System.out.println(name);
        }
        
        //StringUtils 可以支持更多数据类型
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(3);
        ids.add(5);
        String str3 = StringUtils.join(ids, ",");  
        System.out.println(str3);
	}
	
}
OUTPUT

Xiaohong,Xiaoming,Daming,Xiaohei
Xiaohong,Xiaoming,Daming,Xiaohei
Xiaohong
Xiaoming
Daming
Xiaohei
1,3,5

字符串转义

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.6</version>
</dependency>
import org.apache.commons.text.StringEscapeUtils;
public class EscapeString {

	public static void main(String[] args) {
		String str = "He didn't say, \"Stop!\"";
        
		//转义
		String escapedStr = StringEscapeUtils.escapeJava(str);
        System.out.println("escape" + ":" + escapedStr);
        
        //从转义字符串转回来
        String str2 = StringEscapeUtils.unescapeJava(escapedStr);
        System.out.println("unescape" + ":" + str2);
    }	
}
OUTPUT
escape:He didn't say, \"Stop!\"
unescape:He didn't say, "Stop!"

变量名字格式化(驼峰命名)

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.0.1-jre</version>
</dependency>
import com.google.common.base.CaseFormat;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;

public class GuavaUtil {
	public static void main(String[] args) throws Exception {
        List<Integer> list = new ArrayList<Integer>();
        list.add(123);
        list.add(456);
        System.out.println(list);
        
        //直接初始化List数组
        List<Integer> integers = Lists.newArrayList(123, 456);
        System.out.println(integers);

        //拆分字符串,忽略空字符串
        Iterable<String> split = Splitter.on(',')
                .trimResults()
                .omitEmptyStrings()
                .split("123,321,,   abc");

        for (String s : split) {
            System.out.println(s);
        }
        
        //对比常规方法拆分字符串,忽略空字符串
        String a = "123,321,,   abc";
        String[] as = a.split(",");
        
        for(int i=0;i<as.length;i++)
        {
        	if(null == as[i] || as[i].length()<=0)
        	{
        		continue;
        	}
        	else
        	{
        		System.out.println(as[i].trim());
        	}
        }   
        
        //驼峰命名
        String s1 = "CONSTANT_NAME";
        String s2 = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, s1);
        System.out.println(s2); //constantName;    
    }
}

从字符串到输入流

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;

public class String2InputStream {
    public static void main(String[] args) {
        
    	//构造字符串列表
    	List<String> names = new LinkedList<String>();
        names.add("Xiaohong");
        names.add("Xiaoming");
        names.add("Daming");
        names.add("Xiaohei");
        
        //合并为一个字符串,以逗号相连
        String nameStr = String.join(",",names);
        
        //将字符串作为默认的输入流
        InputStream in = IOUtils.toInputStream(nameStr, Charsets.toCharset("UTF-8"));
        //重置系统的输入流
        System.setIn(in);
        
        //模拟键盘输入  这也是OJ平台测试用例输入的原理
        //此处也可以换成一个文件输入流
        Scanner sc=new Scanner(System.in);
        sc.useDelimiter(",");
        while(sc.hasNext())
        {
            System.out.println(sc.next());
        }
        
    }
}

第四章 高级文件处理

4.1 XML DOM解析

可扩展标记语言:意义加数据

DOM/SAX/Stax是JDK自带的解析功能

xml解析方法

树结构:DOM 文档对象模型

流结构:SAX 流机制解析器(推)、Stax 流机制解析器(拉)

DOM方法

其处理方式是将 XML 整个作为类似树结构的方式读入内存中

解析大数据量的 XML 文件,会遇到内存泄露及程序崩溃的风险。

image-20211002133551852

  • DocumentBuilder 解析类,parse方法

  • Node 节点主接口,getChildNodes返回一个NodeList

  • NodeList 节点列表,每个元素是一个Node

  • Document 文档根节点

  • Element 标签节点元素 (每一个标签都是标签节点)

  • Text节点 (包含在XML元素内的,都算Text节点)

  • Attr节点(每个属性节点)

读取dom方法一,自上而下解析:

用到的函数
getNodeName()
getTextContent()
getChildNodes()
Node.ELEMENT_NODE
usersList.item(i)
public static void recursiveTraverse()
{
    try 
    {
        //采用Dom解析xml文件
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.parse("users.xml");

        //获取所有的一级子节点
        NodeList usersList = document.getChildNodes(); //长度为1
        
        //一级节点开始遍历
        for (int i = 0; i < usersList.getLength(); i++) 
        {
            Node users = usersList.item(i);         //1  users 
            NodeList userList = users.getChildNodes(); //获取二级子节点user的列表,长度为9

            //二级节点开始遍历
            for (int j = 0; j < userList.getLength(); j++) //9
            {
                Node user = userList.item(j);
                if (user.getNodeType() == Node.ELEMENT_NODE)
                {
                    NodeList metaList = user.getChildNodes();  //长度为7
                    
                    //三级节点开始遍历
                    for (int k = 0; k < metaList.getLength(); k++) //7
                    {
                        //到最后一级文本
                        Node meta = metaList.item(k);
                        if (meta.getNodeType() == Node.ELEMENT_NODE)
                        {
                            System.out.println(metaList.item(k).getNodeName() 
                                               + ":" + metaList.item(k).getTextContent());
                        }                                                              
                    }                    
                    System.out.println();
                }                   
            }
        }            
    } catch (Exception e) {
        e.printStackTrace();
    } 
} 

读取dom方法二,搜索:

public static void traverseBySearch()
{
    try 
    {
        //采用Dom解析xml文件
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.parse("users.xml");

        Element rootElement = document.getDocumentElement();         

        NodeList nodeList = rootElement.getElementsByTagName("name"); 
        if(nodeList != null) 
        { 
            for (int i = 0 ; i < nodeList.getLength(); i++) 
            { 
                Element element = (Element)nodeList.item(i);                  
                System.out.println(element.getNodeName() + " = " + element.getTextContent());
            } 
        }             
    } catch (Exception e) {
        e.printStackTrace();
    } 	
}  

基于dom技术写xml文件:

public class DomWriter {

	public static void main(String[] args) {
		
		
		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
			
			//新创建一个Document节点
			Document document = dbBuilder.newDocument();
			if (document != null) 
			{
				Element docx = document.createElement("document");	//都是采用Document创建元素		
				Element element = document.createElement("element");
				element.setAttribute("type", "paragraph"); 
				element.setAttribute("alignment", "left"); //element增加2个属性
				
				Element object = document.createElement("object");
				object.setAttribute("type", "text");
				
				Element text = document.createElement("text");
				text.appendChild(document.createTextNode("abcdefg")); //给text节点赋值
				Element bold = document.createElement("bold");
				bold.appendChild(document.createTextNode("true"));    //给bold节点赋值
				
				object.appendChild(text);      //把text节点挂在object下
				object.appendChild(bold);      //把bold节点挂在object下
				element.appendChild(object);   //把object节点挂在element下
				docx.appendChild(element);	   //把element节点挂在docx下		
				document.appendChild(docx);    //把docx挂在document下
				
				TransformerFactory transformerFactory = TransformerFactory.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				DOMSource source = new DOMSource(document);
				
				//定义目标文件
				File file = new File("dom_result.xml");
				StreamResult result = new StreamResult(file);
		 	 
				//将xml内容写入到文件中
				transformer.transform(source, result);
				
				System.out.println("write xml file successfully");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}	
}

运行结果:

<document>
	<element alignment="left" type="paragraph">
		<object type="text">
			<text>abcdefg</text>
			<bold>true</bold>
		</object>
	</element>
</document>

4.2 SAX DOM解析

Simple API for XML

采用事件/流模型来解析 XML 文档,更快速、更轻量。

有选择的解析和访问,不像 DOM 加载整个文档,内存要求较低。

SAX 对 XML 文档的解析为一次性读取,不创建/不存储文档对象,很难同时访问文档中的多处数据。

推模型:当它每发现一个节点就引发一个事件,而我们需要编写这些事件的处理程序,defaulthandler

public class SAXReader {
	public static void main(String[] args) throws SAXException, IOException {
		XMLReader parser = XMLReaderFactory.createXMLReader();
		BookHandler bookHandler = new BookHandler();
		parser.setContentHandler(bookHandler);
		parser.parse("books.xml");
		System.out.println(bookHandler.getNameList());
	}
}

//定义响应程序,必须继承DefaultHandler
class BookHandler extends DefaultHandler {
	private List<String> nameList;
	private boolean title = false;

	public List<String> getNameList() {
		return nameList;
	}

	// xml文档加载时
	public void startDocument() throws SAXException {
		System.out.println("Start parsing document...");
		nameList = new ArrayList<String>();
	}

	// 文档解析结束
	public void endDocument() throws SAXException {
		System.out.println("End");
	}

	// 访问某一个元素,qname指每个尖括号里面的名字 
	public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {

		if (qName.equals("title")) {  //要解析其他元素只需要改这里,比如把“title”改为“year”
			title = true;
		}
	}

	// 结束访问元素
	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
		// End of processing current element
		if (title) {
			title = false;
		}
	}

	// 访问元素正文
	public void characters(char[] ch, int start, int length) {
		
		if (title) {
			String bookTitle = new String(ch, start, length);
			System.out.println("Book title: " + bookTitle);
			nameList.add(bookTitle);
		}
	}

}

4.3 Stax DOM解析

Streaming API for XML

流模型中的拉模型

在遍历文档时,会把感兴趣的部分从读取器中拉出,不需要引发事件,允许我们选择性地处理节点

两套处理API:

基于指针的API, XMLStreamReader

基于迭代器的API,XMLEventReader

方法一,流模式

public static void readByStream() {
    String xmlFile = "books.xml";
    XMLInputFactory factory = XMLInputFactory.newFactory();
    XMLStreamReader streamReader = null;
    try {
        streamReader = factory.createXMLStreamReader(new FileReader(xmlFile));			
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (XMLStreamException e) {
        e.printStackTrace();
    }

    // 基于指针遍历
    try {
        while (streamReader.hasNext()) {
            int event = streamReader.next();
            // 如果是元素的开始
            if (event == XMLStreamConstants.START_ELEMENT) {
                // 列出所有书籍名称
                if ("title".equalsIgnoreCase(streamReader.getLocalName())) {
                    System.out.println("title:" + streamReader.getElementText());
                }
                if ("year".equalsIgnoreCase(streamReader.getLocalName())) {
                    System.out.println("year:" + streamReader.getElementText());
                }
            }
        }
        streamReader.close();
    } catch (XMLStreamException e) {
        e.printStackTrace();
    }
}

方法二,事件模式

有点没看懂

public static void readByEvent() {
    String xmlFile = "books.xml";
    XMLInputFactory factory = XMLInputFactory.newInstance();
    boolean titleFlag = false;
    try {
        // 创建基于迭代器的事件读取器对象
        XMLEventReader eventReader = factory.createXMLEventReader(new FileReader(xmlFile));
        // 遍历Event迭代器
        while (eventReader.hasNext()) {
            XMLEvent event = eventReader.nextEvent();
            // 如果事件对象是元素的开始
            if (event.isStartElement()) {
                // 转换成开始元素事件对象
                StartElement start = event.asStartElement();
                // 打印元素标签的本地名称

                String name = start.getName().getLocalPart();
                //System.out.print(start.getName().getLocalPart());	
                if(name.equals("title"))
                {
                    titleFlag = true;
                    System.out.print("title:");
                }

                // 取得所有属性
                Iterator attrs = start.getAttributes();
                while (attrs.hasNext()) {
                    // 打印所有属性信息
                    Attribute attr = (Attribute) attrs.next();
                    //System.out.print(":" + attr.getName().getLocalPart() + "=" + attr.getValue());
                }
                //System.out.println();
            }

            //如果是正文
            if(event.isCharacters())
            {
                String s = event.asCharacters().getData();
                if(null != s && s.trim().length()>0 && titleFlag)
                {
                    System.out.println(s.trim());
                }					
            }
            //如果事件对象是元素的结束
            if(event.isEndElement())
            {
                EndElement end = event.asEndElement();
                String name = end.getName().getLocalPart();
                if(name.equals("title"))
                {
                    titleFlag = false;
                }
            }
        }
        eventReader.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (XMLStreamException e) {
        e.printStackTrace();
    }
}

4.4 JSON解析

是一种轻量级的数据交换格式,类似XML,更小、更快、更易解析

最早用于Javascript中,容易解析,最后推广到全语言

尽管使用Javascript语法,但是独立于编程语言

三个JSON处理的包:

  • org.json:JSON官方推荐的解析类

  • GSON:Google出品

  • Jackson:号称最快的JSON处理器

Java Bean对象:

  • 具有一个无参的构造函数

  • 可以包括多个属性,所有属性都是private

  • 每个属性都有相应的Getter/Setter方法

OrgJson

  1. 从JSON里put和get数据
public static void testJsonObject() {
    //构造对象
    Person p = new Person();
    p.setName("Tom");
    p.setAge(20);
    p.setScores(Arrays.asList(60,70,80));

    //构造JSONObject对象
    JSONObject obj = new JSONObject(); 	

    //string
    obj.put("name", p.getName());
    //int
    obj.put("age", p.getAge());
    //array
    obj.put("scores", p.getScores());
    //null
    //object.put("null", null);
    System.out.println(obj);        

    System.out.println("name: " + obj.getString("name"));
    System.out.println("age: " + obj.getInt("age"));
    System.out.println("scores: " + obj.getJSONArray("scores"));
}
  1. 从json文件里面读取数据并放到person对象里面
public static void testJsonFile() {
    File file = new File("books.json");
    try (FileReader reader = new FileReader(file)) {
        //读取文件内容到JsonObject对象中
        int fileLen = (int) file.length();
        char[] chars = new char[fileLen];
        reader.read(chars);
        String s = String.valueOf(chars);
        JSONObject jsonObject = new JSONObject(s);

        //开始解析JSONObject对象
        JSONArray books = jsonObject.getJSONArray("books");
        List<Book> bookList = new ArrayList<>();
        for (Object book : books) {
            //获取单个JSONObject对象
            JSONObject bookObject = (JSONObject) book;
            Book book1 = new Book();
            book1.setAuthor(bookObject.getString("author"));
            book1.setYear(bookObject.getString("year"));
            book1.setTitle(bookObject.getString("title"));
            book1.setPrice(bookObject.getInt("price"));
            book1.setCategory(bookObject.getString("category"));
            bookList.add(book1);
        }

        for(Book book:bookList)
        {
            System.out.println(book.getAuthor() + ",  " + book.getTitle());
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}
//books.json
{
  "books": [
    {
      "category": "COOKING",
      "title": "Everyday Italian",
      "author": "Giada De Laurentiis",
      "year": "2005",
      "price": 30.00
    },
    {
      "category": "CHILDREN",
      "title": "Harry Potter",
      "author": "J K. Rowling",
      "year": "2005",
      "price": 29.99
    },
    {
      "category": "WEB",
      "title": "Learning XML",
      "author": "Erik T. Ray",
      "year": "2003",
      "price": 39.95
    }
  ]
}

GSON

  1. 可以通过gson函数很方便的在java对象和json里面直接转换,不需要像org.json一样还要put来get去
public static void testJsonObject() {
    //构造对象
    Person p = new Person();
    p.setName("Tom");
    p.setAge(20);
    p.setScores(Arrays.asList(60,70,80));

    //从Java对象到JSON字符串
    Gson gson = new Gson();
    String s = gson.toJson(p);
    System.out.println(s); //{"name":"Tom","age":20,"scores":[60,70,80]}

    //从JSON字符串到Java对象
    Person p2 = gson.fromJson(s, Person.class);
    System.out.println(p2.getName());  //Tom
    System.out.println(p2.getAge());   //20
    System.out.println(p2.getScores());//[60, 70, 80]

    //调用GSON的JsonObject
    JsonObject json = gson.toJsonTree(p).getAsJsonObject(); //将整个json解析为一颗树
    System.out.println(json.get("name"));  //"Tom"
    System.out.println(json.get("age"));   //20
    System.out.println(json.get("scores"));//[60,70,80]
}
  1. 从json文件中解析数据输入到java对象

第五章 java多线程和并发线程

串行程序,程序只能在单核上运行,无法利用多个cpu

并行程序,程序可以利用多个计算核运行

5.1 多线程的生命周期

java多线程的创建

有两种方法

线程继承(extends)java.lang.Thread类,实现run方法

public class Thread1 extends Thread{
	public void run()
	{
		System.out.println("hello");
	}
	public static void main(String[] a)
	{
		new Thread1().start();
	}
}

线程实现(implement)java.lang.Runnable类,实现run方法

runnable是java四个主要接口之一:Clonable/Comparable/Serializable/Runnable

public class Thread2 implements Runnable{
	public void run()
	{
		System.out.println("hello");
	}
	public static void main(String[] a)
	{
		new Thread(new Thread2()).start();
	}
}
java多线程的启动

两种创建方式不同,启动方式也是不同的

多个线程启动,其启动的先后顺序是随机的

1.通过继承Thread类创建的线程

start方法,会自动以新线程调用run方法,请注意要是直接调用run,会变成串行执行

一个线程对象不能多次start,会报错,多个线程对象都start后,哪一个线程先执行完全由操作系统/JVM来主导

public class ThreadDemo1
{
	public static void main(String args[]) throws Exception
	{
		new TestThread1().start();
		while(true)
		{
			System.out.println("main thread is running");
			Thread.sleep(1000);
		}
	}
}

class TestThread1 extends Thread
{
	public void run() 
	{
		while(true)
		{
			System.out.println(" TestThread1 is running");
			try {
				Thread.sleep(1000); //1000毫秒
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
} 

2.通过实现Runnable类创建的线程

注意Runnable对象必须放在一个Thread类中才能运行,不能直接对一个Runnable对象执行start方法

public class ThreadDemo3
{
	public static void main(String args[])
	{	
		TestThread3 tt= new TestThread3();//创建TestThread类的一个实例
        //new TestThread3().start();会报错
		Thread t= new Thread(tt);//创建一个Thread类的实例
		t.start();//使线程进入Runnable状态
		while(true)
		{
			System.out.println("main thread is running");
			try {
				Thread.sleep(1000); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class TestThread3 implements Runnable //extends Thread
{
	public void run()
	{
		while(true)
		{
			System.out.println(Thread.currentThread().getName() +
			" is running");
			try {
				Thread.sleep(1000); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
java多线程的结束

线程无需关闭,只要run执行完之后就自动结束了

main函数线程可能早于新线程结束,整个程序的终止是等所有线程都中止

5.2 多线程信息共享

粗粒度:子线程与子线程之间和main线程之间缺乏交流

细粒度:线程之间有信息交流通讯(通过共享变量达到信息共享,有两种方法:static变量/同一个Runnable类的成员变量)

static变量实例

public class ThreadDemo0
{
	public static void main(String [] args)
	{
		new TestThread0().start();
		new TestThread0().start();
		new TestThread0().start();
		new TestThread0().start();
	}
}
class TestThread0 extends Thread  
{
	//private int tickets=100;           //每个线程卖100张,没有共享
	private static int tickets=100;  //static变量是共享的,所有的线程共享
	public void run()
	{
		while(true)
		{
			if(tickets>0)
			{
				System.out.println(Thread.currentThread().getName() +
				" is selling ticket " + tickets);
				tickets = tickets - 1;
			}
			else
			{
				break;
			}
		}
	}
}

同一个Runnable类的成员变量实例

注意TestThread1只被创建了一次,就是t,而new Thread只是把同一个t包装成不同的线程对象启动

public class ThreadDemo1
{
	public static void main(String [] args)
	{
		TestThread1 t=new TestThread1();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}
class TestThread1 implements Runnable
{
	private int tickets=100;
	public void run()
	{
		while(true)
		{
			if(tickets>0)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				tickets--;
				System.out.println(Thread.currentThread().getName() +" is selling ticket " + tickets);
			}
			else
			{
				break;
			}
				
		}
	}
}

多线程信息共享问题:

1.工作缓存副本

2.关键步骤缺乏加锁限制

缓存副本解决方法:volatile关键字修饰变量,可以让所有线程都立即看到这个值的变化

public class ThreadDemo2
{
	public static void main(String args[]) throws Exception 
	{
		TestThread2 t = new TestThread2();
		t.start();
		Thread.sleep(2000);
		t.flag = false;
		System.out.println("main thread is exiting");
	}
}

class TestThread2 extends Thread
{
	//boolean flag = true;   //子线程不会停止
	volatile boolean flag = true;  //用volatile修饰的变量可以及时在各线程里面通知
	public void run() 
	{
		int i=0;
		while(flag)
		{i++;}
		System.out.println("test thread3 is exiting");
	}	
} 

加锁限制解决方法:

互斥指某一个线程运行一个代码段,其他线程不能运行

同步指多个线程的运行,必须按照某一种规定的先后顺序,是互斥的特例

synchronized关键字修饰的代码块或函数,只能一个线程进入

public class ThreadDemo3 {
	public static void main(String[] args) {
		TestThread3 t = new TestThread3();
		new Thread(t, "Thread-0").start();
		new Thread(t, "Thread-1").start();
		new Thread(t, "Thread-2").start();
		new Thread(t, "Thread-3").start();
	}
}

class TestThread3 implements Runnable {
	private volatile int tickets = 10; // 多个 线程在共享的

	public void run() {
		while (true) {
			sale();
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
			if (tickets <= 0) {break;}
		}
	}

	public synchronized void sale() { // 同步函数
		if (tickets > 0) {
			System.out.println(Thread.currentThread().getName() + " is saling ticket " + tickets--);
		}
	}
}
//OUTPUT

Thread-0 is saling ticket 10
Thread-3 is saling ticket 9
Thread-2 is saling ticket 8
Thread-1 is saling ticket 7
Thread-1 is saling ticket 6
Thread-2 is saling ticket 5
Thread-3 is saling ticket 4
Thread-0 is saling ticket 3
Thread-3 is saling ticket 2
Thread-2 is saling ticket 1

5.3 多线程管理

image-20211017161310997

线程的阻塞和唤醒

sleep,时间一到,自己会醒来
–wait/notify/notifyAll,等待,需要别人来唤醒
– join,等待另外一个线程结束
– interrupt,向另外一个线程发送中断信号,该线程收到信号,会
触发InterruptedException(可解除阻塞),并进行下一步处
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值