Java核心技术(进阶)

计科19-秦皓东

目录

Java核心技术(进阶)

第一章 Maven

第二章 单元测试和JUnit

第三章 高级文本处理

第四章 高级文件处理

第五章 Java多线程和并发编程

第五章 Java多线程和并发编程(续)

第六章 Java网络编程

第六章 Java网络编程(续)

第七章 数据库编程

第八章 Java混合编程



第一章 Maven

1:构建工具

  • 传统方法和Maven

  • 传统方法优点:第三方库很强大,要学会站在巨人的肩膀上工作

  • 缺点:搜索、确定版本、下载jar包、工作量大且不易

  • 需要手动把jar包添加到项目build path

  • 代码拷贝到别人的机器,需要同样的配置路径

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

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

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

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

  • 当前主要的Java构建工具

  • Maven、Gradle、Builder、Ant等

2:创建Maven项目

3:作业

给定一个汉字句子,可以输出句子的读音。可以借鉴第三方库:pinyin4j 。这个是网址:https://mvnrepository.com/artifact/com.belerweb/pinyin4j 。要求工程是Maven项目。需要Maven编译成功的截图、程序运行的截图、和代码截图

  • 创建依赖

  •  import net.sourceforge.pinyin4j.PinyinHelper;public class Test {public static void main(String[] args) {System.out.println(Test.hanzi("秦皓东"));System.out.println(Test.pinyin("秦皓东"));}​/***获取汉字首字母的方法。如: 张三 --> ZS* @param hz;* @return 大写汉子首字母; 如果都转换失败,那么返回null*/public static String hanzi(String hz){String result=null;if (null!=hz&&!"".equals(hz)){char[] chars=hz.toCharArray();StringBuilder sb=new StringBuilder();for (int i = 0; i < chars.length; i++) {String[] str=PinyinHelper.toHanyuPinyinStringArray(chars[i]);if (str!=null)sb.append(str[0].charAt(0));}if (sb.length()>0)result=sb.toString();}return  result;}​/*** 获取汉字拼音的方法。如: 张三 --> zhangsan* @param py;* @return 汉字拼音; 如果都转换失败,那么返回null*/public static String pinyin(String py){String result=null;if (null!=py&&!"".equals(py)){char[] chars=py.toCharArray();StringBuilder sb=new StringBuilder();for (int i = 0; i < chars.length; i++) {String[] str=PinyinHelper.toHanyuPinyinStringArray(chars[i]);if (str!=null)sb.append(str[0].replaceAll("\\d",""));}if (sb.length()>0)result=sb.toString();}return  result;}}​//结果为:qhd//       qin2hao4dong1 其中的1234分别代表拼音中的一二三四声​

第二章 单元测试和JUnit

1:软件测试

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

  • 单元vs集成测试、白盒vs黑盒测试、自动vs手动测试、回归测试、压力测试

  • 单元测试是指对软件的最小可测试单元,进行检查和验证,通常是一个函数、方法,单元测试是已知代码进行的测试,属于白盒测试

  • 集成测试是将多个单元相互作用,形成一个整体,对整体协调性进行测试

  • 白盒测试,全面了解程序内部逻辑结构,对所有的逻辑路径都进行测试,一般由程序员完成

  • 黑盒测试,又叫功能测试,将程序视为一个不能打开的黑盒。在完全不考虑程序内部结构和特性的情况下,检查程序功能是否按章需求规格说明书的规定正常使用,一般由独立的使用者完成

  • 自动测试:用测试程序批量、反复测试功能程序,并可以自动检查功能程序输出结果是否满足预定要求

  • 手动测试:手动执行程序,手动输入所需要的参数,手动检查程序结果是否满足预定的要求

  • 回归测试:修改旧代码后,重新进行测试试以确定修改没有引入新的错误或导致其他代码产生错误

  • 测试策略(基于main函数的策略)

  • 优点:简单

  • 缺点:无法自动判断被测对象的行为是否符合预期,main方法需要添加大量的代码,这些代码在发布时也需要手动删除,且分散程序员的在开发时的关注点

2:JUnit

  • JUnit是一个Java语言的单元测试框架

  • 问题分析和测试用例

  • 给定三个整数,判断以此为边长能否构成三角形?(三个整数都要大于0,任意两边和大于第三边)

  •   @Testpublic void TriangleTest(){int a=1;int b=2;int c=3;if (a+b>c&&a+c>b&&b+c>a)System.out.println("True");elseSystem.out.println("False");}

3:Maven执行上例子结果

4:作业

  • 编写一个DateUtil的类,里面有一个isLeapYear(int year) 的方法,判断输入年份是否是闰年。如果是闰年,返回true,其他返回false。闰年需要满足以下3个条件:

    年份必须大于0,且小于等于10000。

    年份不能整除100,且可以整除4。

    年份可以整除100,且可以整除400。

  •  public class DateUtil {@Testpublic void isLeapYear(){int a=2021;if (a%100!=0 && a%4==0 || a%100==0 && a%400==0)System.out.println(a+"is LeapYear");elseSystem.out.println(a+"is not LeapTear");}}
  • 运行截图


第三章 高级文本处理

1:字符编码

  • 字符:0,a,我,①,の,······

  • 计算机只用0和1,1 bit(0或者1)

  • ASCII码:用一个字节来存储a~z , A~Z , 0~9和一些常用符号,用于显示英语以及西欧语言

  • 常见ASCII码:回车键(13,00001101),0(48,01000001),A(65,01000001),a(97,01100001)

  • ASCII编码采用1Byte,8bits,最多256个字符,ASCII码无法适用于 其他地方,例如汉字数量由十几万

  • 中文编码:GB18030>GBK>GB2312

  • Unicode 编码(字符集):不断扩充,存储全世界所有的字符

  • 编码方案:UTF-8(兼容ASCII,变长(1-4个字节存储字符),经济,方便传输),UTF-16(用变长(2-4个字节)来存储所有字符),UTF-32(用4个字节存储所有字符)

  • ANSI编码

  • 在Windows上非Unicode的默认编码,在简体中文WindowsOS中,ANSI编码代表GBK,在繁体中文Windows操作系统中,ANSI代表Big5,记事本默认采用的是ASNI保存,ASNI编码文件不能再兼容使用

2:Java的字符编码

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

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

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

3:Java国际化编程

  • Internationalization,缩写为i18n.

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

  • Java是第一个设计成支持国际化的编程语言

  • java.util.RescourceBundle用于加载一个语言_国家语言包

  • java.util.Local定义一个语言_国家

  • java.text.MessageFormat用于格式化带占位符的字符串

  • java.text.NumberFormat用于格式化数字/金额

  • java.text.DataFormat用于格式化日期时间

  • java.time.format.DateTimeFormatter用于格式化日期时间

4:Locale类

  • Locale(zh_CH,en_US)变量:语言:zh,en等 | 国家/地区:CN,US等

  • Locale方法:getAvailableLocales()返回所有的可用的Locale,getDefault()返回默认的Locale

  • //返回Java所支持的全部国家的语言的数组Locale[] localeList=Locale.getAvailableLocales();for (Locale locale:localeList){System.out.println(locale.getLanguage()+"_"+locale.getCountry());System.out.println(locale.getDisplayLanguage()+"_"+locale.getDisplayCountry());}System.out.println("======================");Locale myLocale=Locale.getDefault();System.out.println(myLocale);//zh_CNSystem.out.println(Locale.CHINA);//zh_CNmyLocale=new Locale("en","US");//语言_国家,强制转化成en_USSystem.out.println(myLocale);//en_US

5:语言文件

  • 一个Properties文件,包含K-V对,每行一个K-V,例如:age=20

  • 命名规则:包名+语言+国家地区.properties,(语言和国家地区可选)

  • message.properties

  • message_zh.properties

  • message_zh_CN.properties

  • 存储的文件必须是ASCII码文件,如果是ASCII以外的名字,必须用Unicode的表示\uxxxx

  • 可以采用native2ascii.exe(%JAVA_HOME%\bin目录下)进行转码

6:ResourceBundle类

  • 根据Locale要求,加载语言文件(Properties文件)

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

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

  • ResourceBundle根据key找value的查找路径

  • 包名当前Locale语言当前Locale国家地区当前Locale变量(variant)

  • 包名当前Locale语言当前Locale国家地区

  • 包名当前Locale语言

  • 包名默认Locale语言,默认Locale国家地区默认Locale变量(variant)

  • 包名默认Locale语言默认Locale国家地区

  • 包名默认Locale语言

  • 包名

  • //首先得创建properties文件,点击file其次new-Resourcebundle-按照命名格式命名
    //再输入 hello=\u4f60\u597d,\u4e16\u754c 输出相对应的内容
    ------------------------------------------------------------------------------------------------------
    Locale myLocale=Locale.getDefault();System.out.println(myLocale);//zh_CN
    ​ResourceBundle bundle=ResourceBundle.getBundle("message",myLocale);System.out.println(bundle.getString("hello"));//你好,世界

7:Java高级字符串处理

  • 正则表达式:用事先定义好的一些特定字符,及这些特定字符的组合,组成一个“规则字符串”

  • 正则表达式相关语法:https://www.runoob.com/regexp/regexp-syntax.html

  • 抛出问题:如何识别给定字符串为合法的邮箱地址/如何认定一个字符串满足一定的规律

  • 例:^[A~Z a~z]+$,代表着一个字符串,只能由26英文字母组成

  • 作用:测试字符串的模式,识别/替换文本,提取文本

  • 正则表达式独立于特定语言(Java,python,PHP,Perl·····)

  • 正则表达式的匹配模板:定界符、原子、特殊功能字符、模式修正符

  • 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)

  • import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    ​
    public class A {private  static  final String REGEX="\\bdog\\b";//\b表示边界private static final String INPUT="dog dogp dog doggie dogg ";public static void main(String[] args) {//检查字符串里面有多少个dogPattern p= Pattern.compile(REGEX);Matcher m=p.matcher(INPUT);
    int count=0;
    while(m.find()){count++;System.out.println("Match number: "+count);System.out.println("start(): "+m.start());System.out.println("end(): "+m.end());
    }
    ​}
    }
    //输出结果:Match number: 1
    //        start(): 0
    //        end(): 3
    //        Match number: 2
    //        start(): 9
    //        end(): 12
    ​
    -----------------------------------------------------------------------------------------------------import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    ​
    public class A {private  static  final String REGEX="foo";private static final String INPUT= "foo1";private  static Pattern p;private  static Matcher m;public static void main(String[] args) {
    ​p= Pattern.compile(REGEX);m=p.matcher(INPUT);
    ​System.out.println(m.lookingAt());//部分匹配System.out.println(m.matches());//完全匹配}
    }
    //输出结果:true
    //        false
    ​
    -----------------------------------------------------------------------------------------------------import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    ​
    public class A {private  static   String REGEX="dog";private  static  String REPLACE="cat";private static String INPUT= "The dog says meow. All dogs say meow.";private  static Pattern p;private  static Matcher m;public static void main(String[] args) {
    ​p= Pattern.compile(REGEX);m=p.matcher(INPUT);INPUT=m.replaceAll(REPLACE);//把所有的dog都换成catSystem.out.println(INPUT);}
    }
    //输出结果:The cat says meow. All cats say meow.

8:其他字符串操作

  • 字符串和集合互转:[1,2,3],“1,2,3”

  • 字符串转义:对关键字字符转义

  • 变量名字格式化:名字驼峰命名

  • 字符串输入流:将字符串转为一个输入流

  • import com.sun.deploy.util.StringUtils;
    ​
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    ​
    ​
    public class A {public static void main(String[] args) {//从ArrayList变到字符串List<String> list=new ArrayList<String>();list.add("abc");list.add("def");list.add("ghi");System.out.println(list);String str1=String.join("",list);String str2= StringUtils.join(list,",");System.out.println("str1: "+str1);//str1: abc def ghiSystem.out.println("str2: "+str2);//str2: abc,def,ghi
    ​//从字符串变回ArrayListList<String> list1= Arrays.asList(str2.split(","));for (String str:list1)System.out.print(str);//abcdefghiSystem.out.println();System.out.println(list1.size());//3}}

9:作业

  • 前提:中文操作系统。如果是英文操作系统,则按照题目意思,重新构造输入,完成ResourceBundle搜索路径即可。

    假设有msg_zh.properties (需要经过native2ascii转化)

    ############

    university=华东师范大学

    ############

    msg_zh_CN.properties (需要经过native2ascii转化)

    ############

    name=张三

    ############

    msg_en_US.properties

    #################

    name=Tom

    #################

    仿照第三章第二节NewHelloWorld程序,输出university和name在zh_CN, en_NZ,en_US三种Locale的值。提交程序和运行结果截图(有异常输出,也请截图或者做好try-catch)。

  • import java.util.Locale;
    import java.util.ResourceBundle;
    ​
    public class A {public static void main(String[] args) {Locale myLocale=Locale.getDefault();System.out.println(myLocale);//zh_CN
    ​ResourceBundle bundle=ResourceBundle.getBundle("msg",myLocale);System.out.println(bundle.getString("name"));
    ​ResourceBundle bundle2=ResourceBundle.getBundle("msg",myLocale);System.out.println(bundle2.getString("university"));
    ​myLocale=new Locale("en","US");//语言_国家,强制转化成en_USSystem.out.println(myLocale);//en_USResourceBundle bundle1=ResourceBundle.getBundle("msg",myLocale);System.out.println(bundle1.getString("name"));
    ​}}
    ​

第四章 高级文件处理

1:XML

  • eXtensible Markup Language,www.w3.org

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

  • 标签可自行定义,具有自我描述性

  • 纯文本表示,跨系统/平台/语言

  • W3C标准(1998年发布了XML1.0,包括几乎所有的Unicode字符)

2:XML结构

  • 常规语法

  • 任何的起始标签都必须有一个结束标签

  • 简化写法,例如,<name> </name> 可以写为 <name/>

  • 大小写敏感,如<name>和<Name>不一样

  • 每个文件都要有一个根元素

  • 标签必须按合适的顺序进行嵌套,不可错位

  • 所有的特性都必须有值,且在值的周围加上引号

  • 需要转义字符,如“<”需要用&lt;代替

  • 注释:<!--注释内容-->

  • <bookstore><book category="CHILDREN"><title lang="en">Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book><book category="WEB"><title lang="en">Learning XML</title><author>Erik T. Ray</author><year>2003</year><price>39.95</price></book>
    </bookstore>
    ​

3:XML扩展

  • DTD(Document Type Definition)

  • 定义XML文档的结构

  • 使用一系列合法的元素来定义文档结构

  • 可嵌套在xml文档中,或者在xml中引用

  • XML Schema(XSD,XML Schema Definition)

  • 定义XML文档的结构,DTD的继任者

  • 支持数据类型,可扩展,功能更完善,强大

  • 采用xml编写

  • XSL

  • 扩展样式表语言

  • XSL作用与XML,等同于CSS作用于HTML

  • 内容:XSLT(转换XML文档),XPath(在XML文档中导航),XSL-FO(格式化XML文档)

4:XML解析

  • XML解析方法

  • 树结构:DOM:Document Object Model 文档对象模型,擅长(小规模)读/写

  • 流结构:SAX:Simple API for XML 流机制解释器(推模式),擅长读

  • Stax:流机制解释器(拉模式),擅长读

  • DOM:直观易用,其处理方式是将XML,整个作为类似书结构的方式读入内存中以便操作及解析,方便修改,解析大量数据量的XML,会遇到内存泄漏及程序崩溃的风险

5:DOM类

  • DocumentBuuilder解析类,parse方法

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

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

  • Documen文档根节点

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

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

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

  • package xml.stax;
    ​
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.util.Iterator;
    ​
    import javax.xml.stream.XMLEventReader;
    import javax.xml.stream.XMLInputFactory;
    import javax.xml.stream.XMLStreamConstants;
    import javax.xml.stream.XMLStreamException;
    import javax.xml.stream.XMLStreamReader;
    import javax.xml.stream.events.Attribute;
    import javax.xml.stream.events.EndElement;
    import javax.xml.stream.events.StartElement;
    import javax.xml.stream.events.XMLEvent;
    ​
    ​
    public class StaxReader {public static void main(String[] args) {StaxReader.readByStream();System.out.println("");StaxReader.readByEvent();}
    ​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());}}}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));// 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();}}
    }
    ----------------------------------------------------------------------------------------------------package xml.dom;
    ​
    import java.io.File;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    ​
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    ​
    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();}       }
    }
    ​

6:Stax

  • Streanming API for XML

  • 流模型中的拉模型

  • 在遍历文档时,会把感兴趣的部分从读取器中拉出,不需要引发事件,允许我们选择性地处理节点。这大大提高了灵活性,以及整体效率

  • 两套API:基于指针的API,基于迭代器的API

  • package xml.stax;
    ​
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.util.Iterator;
    ​
    import javax.xml.stream.XMLEventReader;
    import javax.xml.stream.XMLInputFactory;
    import javax.xml.stream.XMLStreamConstants;
    import javax.xml.stream.XMLStreamException;
    import javax.xml.stream.XMLStreamReader;
    import javax.xml.stream.events.Attribute;
    import javax.xml.stream.events.EndElement;
    import javax.xml.stream.events.StartElement;
    import javax.xml.stream.events.XMLEvent;
    ​
    ​
    public class StaxReader {public static void main(String[] args) {StaxReader.readByStream();System.out.println("");StaxReader.readByEvent();}
    ​
    ​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());}}}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));//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();}}
    }

7:SAX

  • Simple API for XML

  • 采用事件/流模型来解析XML文档,更快捷,更轻量

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

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

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

  • package xml.sax;
    ​
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    ​
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.XMLReader;
    import org.xml.sax.helpers.DefaultHandler;
    import org.xml.sax.helpers.XMLReaderFactory;
    ​
    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());}
    }
    ​
    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");}
    ​// 访问某一个元素public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
    ​if (qName.equals("title")) {title = true;}}
    ​// 结束访问元素public void endElement(String namespaceURI, String localName, String qName) throws SAXException {// End of processing current elementif (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);}}
    ​
    }
    ​

8:JSON

  • JavaScript Object Notation,JS对象表示法

  • 是一种轻量级的数据交换格式

  • 类似于XML,更小,更快,更易解析

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

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

  • JSONObject和 JSONArray

  • 名称/值对,如:“firstName”:“John”

  • JSON对象:{“name”:"Jo":"email":“a@b.com”}

  • 数据在键值对中,数据由逗号分隔,花括号保存对象

  • JSON数组:方括号保存数组([{“name”:"Jo":"email":“a@b.com”},{“name”:"Jo":"email":“a@b.com”})

9:Java的JSON处理

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

  • 简单易用,通用性强


第五章 Java多线程和并发编程

1:多进程和多线程的简介

  • 多进程的概念:当前的操作系统都是多任务的OS,每个独立执行的任务就是一个进程,OS将时间划分为多个时间片(时间很短),每个时间片内讲CPU分配给某一个任务,时间片结束,CPU将自动回收,再分配给另外的任务

  • 多进程的优点:可以同时运行多个任务,程序由于IO堵塞时,可以释放CPU,让CPU为其他程序服务,当系统有多个CPU时,可以为多个进程同时服务

  • 多进程的缺点:太笨重,不好管理,不好切换

  • 多线程的概念:一个程序可以包括多个子任务,可串/并行,每个子任务可以称为一个线程,如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作,可以提高程序所获得CPU时间和利用率

  • 多线程和多进程的对比:线程共享数据,线程通讯更高效,线程更轻量级,跟容易切换,多个线程更容易管理

  • 多进程执行:启动两个java.exe,多线程执行:只启动一个Java.exe

2:Java多线程的实现

  • Java语言的JVM允许程序运行多个线程,通过java.lang.Thread类来实现

  • Thread类:每个线程都是通过某个特定的Thread对象的run()方法来完成操作的,经常把run()方法的主题称为线程体,通过start()方法来启动这个线程,而非直接调用run()方法

  • API中创建线程的两种方法:1.继承Thread类 2.实现Runable接口

3:继承Thread类

  • 1.定义子类继承Thread类

  • 2.子类中重写Thread类中的run方法

  • 3.创建Thread子类对象,即创建了线程对象

  • 4.调用线程对象start方法:启动线程,调用run方法

  • ​
    public class ThreadDemo4
    {public static void main(String [] args){TestThread4 t=new TestThread4();t.start();TestThread4 t1=new TestThread4();t1.start();     }
    }
    ​
    class TestThread4 extends Thread
    {public void run(){while(true){System.out.println(Thread.currentThread().getName() +" is running");try {Thread.sleep(1000); //1000毫秒} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
    }
    ​

4:实现Runnable接口

  • 1.定义子类,实现Runnable接口

  • 2.子类中重写Runnable接口中的run方法

  • 3.通过Thread类含参构造器创建线程对象

  • 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中

  • 5.调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法

  • public class ThreadDemo3
    {public static void main(String args[]){//new TestThread3().start();//Runnable对象必须放在一个Thread类中才能运行TestThread3 tt= new TestThread3();//创建TestThread类的一个实例Thread t= new Thread(tt);//创建一个Thread类的实例t.start();//使线程进入Runnable状态while(true){System.out.println("main thread is running");try {Thread.sleep(1000); //1000毫秒} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
    }
    class TestThread3 implements Runnable //extends Thread
    {//线程的代码段,当执行start()时,线程从此出开始执行public void run(){while(true){System.out.println(Thread.currentThread().getName() +" is running");try {Thread.sleep(1000); //1000毫秒} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
    }
    ​

5:线程的生命周期

  • 新建:当一个Thread类或其 子类的对象被声明并创建时,新生的线程对象处于新建状态

  • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义 了线程的操作和功能

  • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻寒状态

  • 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

6:线程的同步

  • Java对于多线程的安全问题提供了专业的解决方式:同步机制

  • Synchronized的使用方法:synchronized可以放在方法声明中,表示整个方法为同步方法

  • 同步代码块:synchronized(对象){//:需要被同步的代码}

  • synchronized的锁:

  • 任意对象都可以作为同步锁。所有对象都自动含有单一-的锁(监视器)

  • 同步方法的锁:静态方法(类名.class) 、非静态方法(this)

  • 同步代码块:自己指定,很多时候也是指定为this或类名.class

  • 注意:必须确保使用同一个资源的多个线程共用一-把锁,这个非常重要,否则就无法保证共享资源的安全

  • ​
    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 = 100; // 多个 线程在共享的String str = new String("");
    ​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--);}}
    }
    ​

7:Java多线程管理

  • 线程的阻塞和唤醒

  • sleep:时间一到自己就会醒来

  • wait/notify/notifyAll,等待,需要别人来唤醒

  • join,等待另外一个线程进来

  • interrupt,向另外一个线程发送中断信号,该线程收到信号会触发InterruptedException(可解除阻塞),并进行下一步处理

  • package interrupt;
    ​
    public class InterruptTest {
    ​public static void main(String[] args) throws InterruptedException {TestThread1 t1 = new TestThread1();TestThread2 t2 = new TestThread2();
    ​t1.start();t2.start();
    ​// 让线程运行一会儿后中断Thread.sleep(2000);t1.interrupt();t2.flag = false;System.out.println("main thread is exiting");}
    ​
    }
    ​
    class TestThread1 extends Thread {public void run() {// 判断标志,当本线程被别人interrupt后,JVM会被本线程设置interrupted标记while (!interrupted()) {System.out.println("test thread1 is running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;}}System.out.println("test thread1 is exiting");}
    }
    ​
    class TestThread2 extends Thread {public volatile boolean flag = true;public void run() {// 判断标志,当本线程被别人interrupt后,JVM会被本线程设置interrupted标记while (flag) {System.out.println("test thread2 is running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("test thread2 is exiting");}
    }
    ​
  • 经典问题:生产者消费者问题

  • package product;
    ​
    /***仓库*/
    class Storage {// 仓库容量为10private Product[] products = new Product[10];private int top = 0;
    ​// 生产者往仓库中放入产品public synchronized void push(Product product) {while (top == products.length) {try {System.out.println("producer wait");wait();//仓库已满,等待} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//把产品放入仓库products[top++] = product;System.out.println(Thread.currentThread().getName() + " 生产了产品"+ product);System.out.println("producer notifyAll");notifyAll();//唤醒等待线程​}
    ​// 消费者从仓库中取出产品public synchronized Product pop() {while (top == 0) {try {System.out.println("consumer wait");wait();//仓库空,等待} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}
    ​}
    ​//从仓库中取产品--top;Product p = new Product(products[top].getId(), products[top].getName());products[top] = null;System.out.println(Thread.currentThread().getName() + " 消费了产品" + p);System.out.println("comsumer notifyAll");notifyAll();//唤醒等待线程return p;}
    }
    ​
    package product;
    /*** 经典生产者与消费者问题* 生产者不断的往仓库中存放产品,消费者从仓库中消费产品。* 其中生产者和消费者都可以有若干个。* 仓库规则:容量有限,库满时不能存放,库空时不能取产品 。*/
    ​
    public class ProductTest {public static void main(String[] args) throws InterruptedException {Storage storage = new Storage();Thread consumer1 = new Thread(new Consumer(storage));consumer1.setName("消费者1");Thread consumer2 = new Thread(new Consumer(storage));consumer2.setName("消费者2");Thread producer1 = new Thread(new Producer(storage));producer1.setName("生产者1");Thread producer2 = new Thread(new Producer(storage));producer2.setName("生产者2");producer1.start();producer2.start();Thread.sleep(1000);     consumer1.start();consumer2.start();      }
    }
    ​
    package product;
    ​
    /*** 产品类*/
    class Product {private int id;// 产品idprivate String name;// 产品名称
    ​public Product(int id, String name) {this.id = id;this.name = name;}public String toString() {return "(产品ID:" + id + " 产品名称:" + name + ")";}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;}
    }
    ​
    package product;
    import java.util.Random;
    ​
    /*** 生产者*/
    class Producer implements Runnable {private Storage storage;
    ​public Producer(Storage storage) {this.storage = storage;}
    ​@Overridepublic void run() {int i = 0;Random r = new Random();while(i<10){i++;Product product = new Product(i, "电话" + r.nextInt(100));storage.push(product);}       }
    }
    ​
    package product;
    /*** 消费者*/
    class Consumer implements Runnable {private Storage storage;
    ​public Consumer(Storage storage) {this.storage = storage;}
    ​public void run() {int i = 0;while(i<10){i++;storage.pop();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
    }
    ​

8:JDK5.0新增线程创建方式

  • 1.实现Callable接口:相比于Runnable接口,Callable接口功能更强大,相比于run方法可以有返回值,方法可以抛出异常,支持泛型

  • 2.使用线程池


第五章 Java多线程和并发编程(续)

1:Java并发框架Executor

  • 业务:任务多,数据量大

  • 串行vs并行:串行编程简单,并行编程困难,单个计算核频率下降,计算核数增多,整体性能变高

  • 线程组管理

  • 线程的集合、树形结构、大线程组可以包括小线程组、能够有效管理多个线程,但管理效率低、任务分配和执行过程高度耦合、重复创建线程,关闭线程操作,无法重用线程

2:Executor

  • JDK5开始提供Excutor Frame Work(java.util.concurrent.*)

  • 分离任务的创建和执行者的创建

  • 线程重复利用(new 线程代价很大)

  • 共享线程池的概念:预设好的多个Thread,可弹性增加、多次执行很多很小的任务、任务创建和执行过程解耦、程序员无需关心线程池执行任务过程

  • 主要类:ExecutorService,ThreadPoolExecutor,Future

  • Executors.newCachedThreadPool/newFixedThreadPool 创建线程池

  • ExecutorService 线程池服务

  • Callable 具体的逻辑对象(线程类)

  • Future返回结果

  • package executor.example2;
    ​
    import java.util.Random;
    import java.util.concurrent.Callable;
    ​
    public class SumTask implements Callable<Integer> {//定义每个线程计算的区间private int startNumber;private int endNumber;public SumTask(int startNumber, int endNumber){this.startNumber=startNumber;this.endNumber=endNumber;}@Overridepublic Integer call() throws Exception {int sum = 0;for(int i=startNumber; i<=endNumber; i++){sum = sum + i;}Thread.sleep(new Random().nextInt(1000));System.out.printf("%s: %d\n",Thread.currentThread().getName(),sum);return sum;}
    }
    ​
    package executor.example2;
    ​
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.ThreadPoolExecutor;
    ​
    ​
    public class SumTest {
    ​public static void main(String[] args) {// 执行线程池ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(4);List<Future<Integer>> resultList=new ArrayList<>();
    ​//统计1-1000总和,分成10个任务计算,提交任务for (int i=0; i<10; i++){SumTask calculator=new SumTask(i*100+1, (i+1)*100);Future<Integer> result=executor.submit(calculator);resultList.add(result);}// 每隔50毫秒,轮询等待10个任务结束do {System.out.printf("Main: 已经完成多少个任务: %d\n",executor.getCompletedTaskCount());for (int i=0; i<resultList.size(); i++) {Future<Integer> result=resultList.get(i);System.out.printf("Main: Task %d: %s\n",i,result.isDone());}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}} while (executor.getCompletedTaskCount()<resultList.size());// 所有任务都已经结束了,综合计算结果        int total = 0;for (int i=0; i<resultList.size(); i++) {Future<Integer> result=resultList.get(i);Integer sum=null;try {sum=result.get();total = total + sum;} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}System.out.printf("1-1000的总和:" + total);// 关闭线程池executor.shutdown();}
    }
    ​

3:Java并发框架Fork-join

  • ​
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    ​
    //分任务求和
    public class SumTest {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建执行线程池ForkJoinPool pool = new ForkJoinPool();//ForkJoinPool pool = new ForkJoinPool(4);//创建任务SumTask task = new SumTask(1, 10000000);//提交任务ForkJoinTask<Long> result = pool.submit(task);//等待结果do {System.out.printf("Main: Thread Count: %d\n",pool.getActiveThreadCount());System.out.printf("Main: Paralelism: %d\n",pool.getParallelism());try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}} while (!task.isDone());//输出结果System.out.println(result.get().toString());}
    }
    ​
    import java.math.BigInteger;
    import java.util.concurrent.RecursiveTask;
    ​
    //分任务求和
    public class SumTask extends RecursiveTask<Long> {private int start;private int end;
    ​public SumTask(int start, int end) {this.start = start;this.end = end;}
    ​public static final int threadhold = 5;
    ​@Overrideprotected Long compute() {Long sum = 0L;// 如果任务足够小, 就直接执行boolean canCompute = (end - start) <= threadhold;if (canCompute) {for (int i = start; i <= end; i++) {sum = sum + i;              }} else {// 任务大于阈值, 分裂为2个任务int middle = (start + end) / 2;SumTask subTask1 = new SumTask(start, middle);SumTask subTask2 = new SumTask(middle + 1, end);
    ​invokeAll(subTask1, subTask2);
    ​Long sum1 = subTask1.join();Long sum2 = subTask2.join();
    ​// 结果合并sum = sum1 + sum2;}return sum;}
    }
    ​

4:Java并发数据结构

  • 常用的数据结构(ArrayList、Hashset)线程是不安全的

  • 传统的Vector、Hashtable等同步集合但是效率过低

  • 并发数据结构:数据添加和删除

  • 阻塞式集合(当集合为空或者满时,等待)、非阻塞式集合(当集合为空时或者满时,不等待,返回null或异常)

  • List

  • package list;
    ​
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    ​
    public class ListTest {    public static void main(String[] args) throws InterruptedException{
    ​//线程不安全List<String> unsafeList = new ArrayList<String>();//线程安全List<String> safeList1 = Collections.synchronizedList(new ArrayList<String>());//线程安全CopyOnWriteArrayList<String> safeList2 = new CopyOnWriteArrayList<String>();
    ​ListThread t1 = new ListThread(unsafeList);ListThread t2 = new ListThread(safeList1);ListThread t3 = new ListThread(safeList2);
    ​for(int i = 0; i < 10; i++){Thread t = new Thread(t1, String.valueOf(i));t.start();}for(int i = 0; i < 10; i++) {Thread t = new Thread(t2, String.valueOf(i));t.start();}for(int i = 0; i < 10; i++) {Thread t = new Thread(t3, String.valueOf(i));t.start();}
    ​//等待子线程执行完Thread.sleep(2000);System.out.println("listThread1.list.size() = " + t1.list.size());System.out.println("listThread2.list.size() = " + t2.list.size());System.out.println("listThread3.list.size() = " + t3.list.size());
    ​//输出list中的值System.out.println("unsafeList:");for(String s : t1.list){if(s == null){System.out.print("null  ");}else{System.out.print(s + "  ");}}System.out.println();System.out.println("safeList1:");for(String s : t2.list){if(s == null){System.out.print("null  ");}else{System.out.print(s + "  ");}}System.out.println();System.out.println("safeList2:");for(String s : t3.list){if(s == null){System.out.print("null  ");}else{System.out.print(s + "  ");}}}
    }
    ​
    class ListThread implements Runnable{public List<String> list;
    ​public ListThread(List<String> list){this.list = list;}
    ​@Overridepublic void run() {int i = 0;while(i<10){try {Thread.sleep(10);}catch (InterruptedException e){e.printStackTrace();}//把当前线程名称加入list中list.add(Thread.currentThread().getName());i++;}        }
    }
  • set

  • package set;
    ​
    import java.util.*;
    import java.util.concurrent.CopyOnWriteArraySet;
    ​
    public class SetTest{  public static void main(String[] args) throws InterruptedException{
    ​//线程不安全Set<String> unsafeSet = new HashSet<String>();//线程安全Set<String> safeSet1 = Collections.synchronizedSet(new HashSet<String>());//线程安全CopyOnWriteArraySet<String> safeSet2 = new CopyOnWriteArraySet<String>();
    ​SetThread t1 = new SetThread(unsafeSet);SetThread t2 = new SetThread(safeSet1);SetThread t3 = new SetThread(safeSet2);}
    }
  • map

  • package map;
    ​
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    ​
    public class MapTest{    public static void main(String[] args) throws InterruptedException{
    ​//线程不安全Map<Integer,String> unsafeMap = new HashMap<Integer,String>();//线程安全Map<Integer,String> safeMap1 = Collections.synchronizedMap(new HashMap<Integer,String>());//线程安全ConcurrentHashMap<Integer,String> safeMap2 = new ConcurrentHashMap<Integer,String>();
    ​MapThread t1 = new MapThread(unsafeMap);MapThread t2 = new MapThread(safeMap1);MapThread t3 = new MapThread(safeMap2);}}
  • queue

  • package queue;
    ​
    import java.util.*;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ConcurrentLinkedDeque;
    ​
    public class QueueTest {
    ​public static void main(String[] args) throws InterruptedException{
    ​//线程不安全Deque<String> unsafeQueue = new ArrayDeque<String>();//线程安全ConcurrentLinkedDeque<String> safeQueue1 = new ConcurrentLinkedDeque<String>();
    ​ArrayBlockingQueue<String> safeQueue2 = new ArrayBlockingQueue<String>(100);
    ​QueueThread t1 = new QueueThread(unsafeQueue);QueueThread t2 = new QueueThread(safeQueue1);QueueThread t3 = new QueueThread(safeQueue2);}
    }

5:Java并发协作控制

  • 线程协作:Thread、Executor、Fork-join、synchronized

  • Lock也可以实现同步的效果

  • ​
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    ​
    public class LockExample {
    ​private static final ReentrantLock queueLock = new ReentrantLock(); //可重入锁private static final ReentrantReadWriteLock orderLock = new ReentrantReadWriteLock(); //可重入读写锁/*** 有家奶茶店,点单有时需要排队 * 假设想买奶茶的人如果看到需要排队,就决定不买* 又假设奶茶店有老板和多名员工,记单方式比较原始,只有一个订单本* 老板负责写新订单,员工不断地查看订单本得到信息来制作奶茶,在老板写新订单时员工不能看订单本* 多个员工可同时看订单本,在员工看时老板不能写新订单* @param args* @throws InterruptedException */public static void main(String[] args) throws InterruptedException {//buyMilkTea();handleOrder(); //需手动关闭}public void tryToBuyMilkTea() throws InterruptedException {boolean flag = true;while(flag){if (queueLock.tryLock()) {//queueLock.lock();long thinkingTime = (long) (Math.random() * 500);Thread.sleep(thinkingTime);System.out.println(Thread.currentThread().getName() + ": 来一杯珍珠奶茶,不要珍珠");flag = false;queueLock.unlock();} else {//System.out.println(Thread.currentThread().getName() + ":" + queueLock.getQueueLength() + "人在排队");System.out.println(Thread.currentThread().getName() + ": 再等等");}if(flag){Thread.sleep(1000);}}}public void addOrder() throws InterruptedException {orderLock.writeLock().lock();long writingTime = (long) (Math.random() * 1000);Thread.sleep(writingTime);System.out.println("老板新加一笔订单");orderLock.writeLock().unlock();}public void viewOrder() throws InterruptedException {orderLock.readLock().lock();long readingTime = (long) (Math.random() * 500);Thread.sleep(readingTime);System.out.println(Thread.currentThread().getName() + ": 查看订单本");orderLock.readLock().unlock();
    ​}public static void buyMilkTea() throws InterruptedException {LockExample lockExample = new LockExample();int STUDENTS_CNT = 10;Thread[] students = new Thread[STUDENTS_CNT];for (int i = 0; i < STUDENTS_CNT; i++) {students[i] = new Thread(new Runnable() {
    ​@Overridepublic void run() {try {long walkingTime = (long) (Math.random() * 1000);Thread.sleep(walkingTime);lockExample.tryToBuyMilkTea();} catch(InterruptedException e) {System.out.println(e.getMessage());}}});students[i].start();}for (int i = 0; i < STUDENTS_CNT; i++)students[i].join();
    ​}public static void handleOrder() throws InterruptedException {LockExample lockExample = new LockExample();Thread boss = new Thread(new Runnable() {
    ​@Overridepublic void run() {while (true) {try {lockExample.addOrder();long waitingTime = (long) (Math.random() * 1000);Thread.sleep(waitingTime);} catch (InterruptedException e) {System.out.println(e.getMessage());}}}});boss.start();
    ​int workerCnt = 3;Thread[] workers = new Thread[workerCnt];for (int i = 0; i < workerCnt; i++){workers[i] = new Thread(new Runnable() {
    ​@Overridepublic void run() {while (true) {try {lockExample.viewOrder();long workingTime = (long) (Math.random() * 5000);Thread.sleep(workingTime);} catch (InterruptedException e) {System.out.println(e.getMessage());}}}});workers[i].start();}}
    }
    ​
  • Semaphore

  • import java.util.concurrent.Semaphore;
    ​
    public class SemaphoreExample {
    ​private final Semaphore placeSemaphore = new Semaphore(5);public boolean parking() throws InterruptedException {if (placeSemaphore.tryAcquire()) {System.out.println(Thread.currentThread().getName() + ": 停车成功");return true;} else {System.out.println(Thread.currentThread().getName() + ": 没有空位");return false;}
    ​}public void leaving() throws InterruptedException {placeSemaphore.release();System.out.println(Thread.currentThread().getName() + ": 开走");}/*** 现有一地下车库,共有车位5个,由10辆车需要停放,每次停放时,去申请信号量* @param args* @throws InterruptedException */public static void main(String[] args) throws InterruptedException {int tryToParkCnt = 10;SemaphoreExample semaphoreExample = new SemaphoreExample();Thread[] parkers = new Thread[tryToParkCnt];for (int i = 0; i < tryToParkCnt; i++) {parkers[i] = new Thread(new Runnable() {
    ​@Overridepublic void run() {try {long randomTime = (long) (Math.random() * 1000);Thread.sleep(randomTime);if (semaphoreExample.parking()) {long parkingTime = (long) (Math.random() * 1200);Thread.sleep(parkingTime);semaphoreExample.leaving();}} catch (InterruptedException e) {e.printStackTrace();}}});parkers[i].start();}
    ​for (int i = 0; i < tryToParkCnt; i++) {parkers[i].join();}   }
    }
    ​
  • Latch

  • import java.util.concurrent.CountDownLatch;
    ​
    public class CountDownLatchExample {
    ​/*** 设想百米赛跑比赛 发令枪发出信号后选手开始跑,全部选手跑到终点后比赛结束* * @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {int runnerCnt = 10;CountDownLatch startSignal = new CountDownLatch(1);CountDownLatch doneSignal = new CountDownLatch(runnerCnt);
    ​for (int i = 0; i < runnerCnt; ++i) // create and start threadsnew Thread(new Worker(startSignal, doneSignal)).start();
    ​System.out.println("准备工作...");System.out.println("准备工作就绪");startSignal.countDown(); // let all threads proceedSystem.out.println("比赛开始");doneSignal.await(); // wait for all to finishSystem.out.println("比赛结束");}
    ​static class Worker implements Runnable {private final CountDownLatch startSignal;private final CountDownLatch doneSignal;
    ​Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal = startSignal;this.doneSignal = doneSignal;}
    ​public void run() {try {startSignal.await();doWork();doneSignal.countDown();} catch (InterruptedException ex) {} // return;}
    ​void doWork() {System.out.println(Thread.currentThread().getName() + ": 跑完全程");}}
    }
    ​
  • Barrier

  • ​
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    ​
    public class CyclicBarrierExample {/*** 假定有三行数,用三个线程分别计算每一行的和,最终计算总和* @param args*/public static void main(String[] args) {final int[][] numbers = new int[3][5];final int[] results = new int[3];int[] row1 = new int[]{1, 2, 3, 4, 5};int[] row2 = new int[]{6, 7, 8, 9, 10};int[] row3 = new int[]{11, 12, 13, 14, 15};numbers[0] = row1;numbers[1] = row2;numbers[2] = row3;CalculateFinalResult finalResultCalculator = new CalculateFinalResult(results);CyclicBarrier barrier = new CyclicBarrier(3, finalResultCalculator);//当有3个线程在barrier上await,就执行finalResultCalculatorfor(int i = 0; i < 3; i++) {CalculateEachRow rowCalculator = new CalculateEachRow(barrier, numbers, i, results);new Thread(rowCalculator).start();}       }
    }
    ​
    class CalculateEachRow implements Runnable {
    ​final int[][] numbers;final int rowNumber;final int[] res;final CyclicBarrier barrier;CalculateEachRow(CyclicBarrier barrier, int[][] numbers, int rowNumber, int[] res) {this.barrier = barrier;this.numbers = numbers;this.rowNumber = rowNumber;this.res = res;}@Overridepublic void run() {int[] row = numbers[rowNumber];int sum = 0;for (int data : row) {sum += data;res[rowNumber] = sum;}try {System.out.println(Thread.currentThread().getName() + ": 计算第" + (rowNumber + 1) + "行结束,结果为: " + sum);barrier.await(); //等待!只要超过3个(Barrier的构造参数),就放行。} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}}
    ​
    ​
    class CalculateFinalResult implements Runnable {final int[] eachRowRes;int finalRes;public int getFinalResult() {return finalRes;}CalculateFinalResult(int[] eachRowRes) {this.eachRowRes = eachRowRes;}@Overridepublic void run() {int sum = 0;for(int data : eachRowRes) {sum += data;}finalRes = sum;System.out.println("最终结果为: " + finalRes);}}
  • Phaser

  • import java.util.concurrent.Phaser;
    ​
    public class PhaserExample {
    ​/*** 假设举行考试,总共三道大题,每次下发一道题目,等所有学生完成后再进行下一道* * @param args*/public static void main(String[] args) {
    ​int studentsCnt = 5;Phaser phaser = new Phaser(studentsCnt);
    ​for (int i = 0; i < studentsCnt; i++) {new Thread(new Student(phaser)).start();}}
    }
    ​
    class Student implements Runnable {
    ​private final Phaser phaser;
    ​public Student(Phaser phaser) {this.phaser = phaser;}
    ​@Overridepublic void run() {try {doTesting(1);phaser.arriveAndAwaitAdvance(); //等到5个线程都到了,才放行doTesting(2);phaser.arriveAndAwaitAdvance();doTesting(3);phaser.arriveAndAwaitAdvance();} catch (InterruptedException e) {e.printStackTrace();}}
    ​private void doTesting(int i) throws InterruptedException {String name = Thread.currentThread().getName();System.out.println(name + "开始答第" + i + "题");long thinkingTime = (long) (Math.random() * 1000);Thread.sleep(thinkingTime);System.out.println(name + "第" + i + "道题答题结束");}
    }
  • Exchanger

  • ​
    import java.util.Scanner;
    import java.util.concurrent.Exchanger;
    ​
    public class ExchangerExample {/*** 本例通过Exchanger实现学生成绩查询,简单线程间数据的交换* @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {Exchanger<String> exchanger = new Exchanger<String>();BackgroundWorker worker = new BackgroundWorker(exchanger);new Thread(worker).start();Scanner scanner = new Scanner(System.in);while(true) {System.out.println("输入要查询的属性学生姓名:");String input = scanner.nextLine().trim();exchanger.exchange(input); //把用户输入传递给线程String value = exchanger.exchange(null); //拿到线程反馈结果if ("exit".equals(value)) {break;}System.out.println("查询结果:" + value);}scanner.close();}
    }
    ​
    class BackgroundWorker implements Runnable {
    ​final Exchanger<String> exchanger;BackgroundWorker(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {while (true) {try {String item = exchanger.exchange(null);switch (item) {case "zhangsan": exchanger.exchange("90");break;case "lisi":exchanger.exchange("80");break;case "wangwu":exchanger.exchange("70");break;case "exit":exchanger.exchange("exit");return;default:exchanger.exchange("查无此人");}                   } catch (InterruptedException e) {e.printStackTrace();}               }}
    }
    ​

6:Java定时任务执行

  • 简单定时器机制

  • package timer;
    ​
    import java.util.Calendar;
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    ​
    public class TimerTest {public static void main(String[] args) throws InterruptedException {MyTask task = new MyTask();Timer timer = new Timer();System.out.println("当前时间:"+new Date().toLocaleString());//当前时间1秒后,每2秒执行一次timer.schedule(task, 1000, 2000);Thread.sleep(10000);task.cancel();  //取消当前的任务System.out.println("================================");Calendar now = Calendar.getInstance();now.set(Calendar.SECOND,now.get(Calendar.SECOND)+3);Date runDate = now.getTime();MyTask2 task2 = new MyTask2();timer.scheduleAtFixedRate(task2,runDate,3000); //固定速率Thread.sleep(20000);timer.cancel();  //取消定时器}
    }
    ​
    class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}
    }
    ​
    class MyTask2 extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}}}
    ​

第六章 Java网络编程

1:网络基础知识

  • IP地址:每个机器/网卡都有一个或者多个IP地址

  • IP地址可以按IPV4和IPV6分,可以按公网地址(万维网使用)、私有地址分(局域网使用)

  • 端口号(port):标识正在计算机上运行的进程

  • 端口分类:公认端口、注册端口、动态/私有端口

  • 端口号和IP地址的组合得一个网络套接字:Socket

  • 网络协议:TCP协议、UDP协议、TCP/IP协议簇

2:UDP编程

  • 计算机通讯:数据从一个IP的port出发(发送方),运输到另外一个IP的port(接收方)

  • UDP:无连接无状态的通讯协议

  • DatagramSocket:send和receive方法

  • DatagramPacket:集装箱(封装数据),地址标签(目的地的IP+Port)

  • import java.net.*;
    public class UdpSend
    {public static void main(String [] args) throws Exception{DatagramSocket ds=new DatagramSocket();String str="hello world";DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("127.0.0.1"),3000);System.out.println("UdpSend: 我要发送信息");ds.send(dp);System.out.println("UdpSend: 我发送信息结束");Thread.sleep(1000);byte [] buf=new byte[1024];DatagramPacket dp2=new DatagramPacket(buf,1024);System.out.println("UdpSend: 我在等待信息");ds.receive(dp2);System.out.println("UdpSend: 我接收到信息");String str2=new String(dp2.getData(),0,dp2.getLength()) +" from " + dp2.getAddress().getHostAddress()+":"+dp2.getPort(); System.out.println(str2);ds.close();}
    }
  • import java.net.*;
    public class UdpRecv
    {public static void main(String[] args) throws Exception{DatagramSocket  ds=new DatagramSocket(3000);byte [] buf=new byte[1024];DatagramPacket dp=new DatagramPacket(buf,1024);System.out.println("UdpRecv: 我在等待信息");ds.receive(dp);System.out.println("UdpRecv: 我接收到信息");String strRecv=new String(dp.getData(),0,dp.getLength()) +" from " + dp.getAddress().getHostAddress()+":"+dp.getPort(); System.out.println(strRecv);Thread.sleep(1000);System.out.println("UdpRecv: 我要发送信息");String str="hello world 222";DatagramPacket dp2=new DatagramPacket(str.getBytes(),str.length(), InetAddress.getByName("127.0.0.1"),dp.getPort());ds.send(dp2);System.out.println("UdpRecv: 我发送信息结束");ds.close();}
    }

3:TCP编程

  • TCP协议:有链接、保证可靠的无误差通讯

  • ①服务器:创建一个ServerSocket, 等待连接

  • ②客户机:创建一个Socket, 连接到服务器

  • ③服务器: ServerSocket接收到连接,创建一个Socket和客户的Socket建立专线连接,后续服务器和客户机的对话(这一对Socket)会在一个单独的线程(服务器端)上运行

  • ④服务器的ServerSocket继续等待连接,返回①

  • ServerSocket: 服务器码头

  • 需要绑定port

  • 如果有多块网卡,需要绑定一个IP地址

  • Socket:运输通道客户端需要绑定服务器的地址和Po

  • 客户端往Socket输入流写入数据,送到服务端一客户端从Socket输出流取服务器端过来的数据一服务端反之亦然

  • import java.net.*;
    import java.io.*;
    public class TcpServer
    {public static void main(String [] args) {try{ServerSocket ss=new ServerSocket(8001); //驻守在8001端口Socket s=ss.accept();                   //阻塞,等到有客户端连接上来System.out.println("welcome to the java world");InputStream ips=s.getInputStream();     //有人连上来,打开输入流OutputStream ops=s.getOutputStream();   //打开输出流//同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流ops.write("Hello, Client!".getBytes());  //输出一句话给客户端BufferedReader br = new BufferedReader(new InputStreamReader(ips));//从客户端读取一句话         System.out.println("Client said: " + br.readLine());ips.close();          ops.close();s.close();ss.close();}catch(Exception e){e.printStackTrace();}}
    }

4:HTTP编程

  • 超文本传输数据协议:资源文件采用HTML编写,以URL形式对外提供

  • HTTP访问方式:GET(从服务器获取资源到客户端)、POST(从客户端向服务器发送数据)

  • import java.io.*;
    import java.net.*;
    import java.util.*;
    ​
    public class URLConnectionGetTest
    {public static void main(String[] args){try{String urlName = "http://www.baidu.com";
    ​URL url = new URL(urlName);URLConnection connection = url.openConnection(); connection.connect();
    ​// 打印http的头部信息
    ​Map<String, List<String>> headers = connection.getHeaderFields();for (Map.Entry<String, List<String>> entry : headers.entrySet()){String key = entry.getKey();for (String value : entry.getValue())System.out.println(key + ": " + value);}
    ​// 输出将要收到的内容属性信息
    ​System.out.println("----------");System.out.println("getContentType: " + connection.getContentType());System.out.println("getContentLength: " + connection.getContentLength());System.out.println("getContentEncoding: " + connection.getContentEncoding());System.out.println("getDate: " + connection.getDate());System.out.println("getExpiration: " + connection.getExpiration());System.out.println("getLastModifed: " + connection.getLastModified());System.out.println("----------");
    ​BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
    ​// 输出收到的内容String line = "";while((line=br.readLine()) != null){System.out.println(line);}br.close();}catch (IOException e){e.printStackTrace();}}
    }
  • import java.io.*;
    import java.net.*;
    import java.nio.file.*;
    import java.util.*;
    ​
    public class URLConnectionPostTest
    {public static void main(String[] args) throws IOException{String urlString = "https://tools.usps.com/go/ZipLookupAction.action";Object userAgent = "HTTPie/0.9.2";Object redirects = "1";CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));Map<String, String> params = new HashMap<String, String>();params.put("tAddress", "1 Market Street");  params.put("tCity", "San Francisco");params.put("sState", "CA");String result = doPost(new URL(urlString), params, userAgent == null ? null : userAgent.toString(), redirects == null ? -1 : Integer.parseInt(redirects.toString()));System.out.println(result);}
    ​public static String doPost(URL url, Map<String, String> nameValuePairs, String userAgent, int redirects)throws IOException{        HttpURLConnection connection = (HttpURLConnection) url.openConnection();if (userAgent != null)connection.setRequestProperty("User-Agent", userAgent);if (redirects >= 0)connection.setInstanceFollowRedirects(false);connection.setDoOutput(true);//输出请求的参数try (PrintWriter out = new PrintWriter(connection.getOutputStream())){boolean first = true;for (Map.Entry<String, String> pair : nameValuePairs.entrySet()){//参数必须这样拼接 a=1&b=2&c=3if (first) {first = false;}else{out.print('&');}String name = pair.getKey();String value = pair.getValue();out.print(name);out.print('=');out.print(URLEncoder.encode(value, "UTF-8"));}}      String encoding = connection.getContentEncoding();if (encoding == null) {encoding = "UTF-8";}if (redirects > 0){int responseCode = connection.getResponseCode();System.out.println("responseCode: " + responseCode);if (responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP|| responseCode == HttpURLConnection.HTTP_SEE_OTHER) {String location = connection.getHeaderField("Location");if (location != null){URL base = connection.getURL();connection.disconnect();return doPost(new URL(base, location), nameValuePairs, userAgent, redirects - 1);}}}else if (redirects == 0){throw new IOException("Too many redirects");}//接下来获取html 内容StringBuilder response = new StringBuilder();try (Scanner in = new Scanner(connection.getInputStream(), encoding)){while (in.hasNextLine()){response.append(in.nextLine());response.append("\n");}         }catch (IOException e){InputStream err = connection.getErrorStream();if (err == null) throw e;try (Scanner in = new Scanner(err)){response.append(in.nextLine());response.append("\n");}}
    ​return response.toString();}
    }
    ​
  • //post.properties
    # https://www.ssa.gov/locator
    # url=https://secure.ssa.gov/ICON/ic001.do
    # zipCodeSearched=94118
    url=https://tools.usps.com/go/ZipLookupAction.action
    User-Agent=HTTPie/0.9.2
    redirects=10
    tCompany=
    tAddress=1 Market Street
    tApt=
    tCity=San Francisco
    sState=CA
    mode=1  
  • HttpClient

  • import java.io.IOException;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.charset.Charset;
    ​
    public class JDKHttpClientGetTest {
    ​public static void main(String[] args) throws IOException, InterruptedException {doGet();}public static void doGet() {try{HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());}catch(Exception e) {e.printStackTrace();}}
    }
    ​
    ​
    import java.io.IOException;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    ​
    public class JDKHttpClientPostTest {
    ​public static void main(String[] args) throws IOException, InterruptedException {doPost();}public static void doPost() {try {HttpClient client = HttpClient.newBuilder().build();HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://tools.usps.com/go/ZipLookupAction.action"))//.header("Content-Type","application/x-www-form-urlencoded").header("User-Agent", "HTTPie/0.9.2").header("Content-Type","application/x-www-form-urlencoded;charset=utf-8")//.method("POST", HttpRequest.BodyPublishers.ofString("tAddress=1 Market Street&tCity=San Francisco&sState=CA"))//.version(Version.HTTP_1_1).POST(HttpRequest.BodyPublishers.ofString("tAddress=" + URLEncoder.encode("1 Market Street", "UTF-8") + "&tCity=" + URLEncoder.encode("San Francisco", "UTF-8") + "&sState=CA"))//.POST(HttpRequest.BodyPublishers.ofString("tAddress=" + URLEncoder.encode("1 Market Street", "UTF-8") + "&tCity=" + URLEncoder.encode("San Francisco", "UTF-8") + "&sState=CA")).build();HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.statusCode());System.out.println(response.headers());System.out.println(response.body().toString());
    ​}catch(Exception e) {e.printStackTrace();}}
    }
    ​
  • HttpComponents

  • ​
    import java.io.IOException;
    ​
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.ResponseHandler;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    ​
    public class HttpComponentsGetTest {
    ​public final static void main(String[] args) throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000)   //设置连接超时时间.setConnectionRequestTimeout(5000) // 设置请求超时时间.setSocketTimeout(5000).setRedirectsEnabled(true)//默认允许自动重定向.build();HttpGet httpGet = new HttpGet("http://www.baidu.com");httpGet.setConfig(requestConfig);String srtResult = "";try {HttpResponse httpResponse = httpClient.execute(httpGet);if(httpResponse.getStatusLine().getStatusCode() == 200){srtResult = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");//获得返回的结果                System.out.println(srtResult);}else{//异常处理}} catch (IOException e) {e.printStackTrace();}finally {try {httpClient.close();} catch (IOException e) {e.printStackTrace();}}}
    }
    ​
    ​
    ​
    import java.io.IOException;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.List;
    ​
    import org.apache.http.HttpResponse;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.impl.client.LaxRedirectStrategy;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;
    ​
    public class HttpComponentsPostTest {
    ​public final static void main(String[] args) throws Exception {//获取可关闭的 httpCilent//CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();//配置超时时间RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(10000).setConnectionRequestTimeout(10000).setSocketTimeout(10000).setRedirectsEnabled(false).build();HttpPost httpPost = new HttpPost("https://tools.usps.com/go/ZipLookupAction.action");//设置超时时间httpPost.setConfig(requestConfig);//装配post请求参数List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>(); list.add(new BasicNameValuePair("tAddress", URLEncoder.encode("1 Market Street", "UTF-8")));  //请求参数list.add(new BasicNameValuePair("tCity", URLEncoder.encode("San Francisco", "UTF-8"))); //请求参数list.add(new BasicNameValuePair("sState", "CA")); //请求参数try {UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,"UTF-8"); //设置post求情参数httpPost.setEntity(entity);httpPost.setHeader("User-Agent", "HTTPie/0.9.2");//httpPost.setHeader("Content-Type","application/form-data");HttpResponse httpResponse = httpClient.execute(httpPost);String strResult = "";if(httpResponse != null){ System.out.println(httpResponse.getStatusLine().getStatusCode());if (httpResponse.getStatusLine().getStatusCode() == 200) {strResult = EntityUtils.toString(httpResponse.getEntity());}else {strResult = "Error Response: " + httpResponse.getStatusLine().toString();} }else{}System.out.println(strResult);} catch (Exception e) {e.printStackTrace();}finally {try {if(httpClient != null){httpClient.close(); //释放资源}} catch (IOException e) {e.printStackTrace();}}}
    }

第六章 Java网络编程(续)

1:Java NIO编程

  • BIO:传统的TCP和UDP通讯:Blocking I/O

  • NIO:Non-Blocking I/O

  • NIO三大组件:Buffer/Channel/Selector

  • package nio;
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    ​
    public class NioServer {
    ​public static void main(String[] args) throws IOException {int port = 8001;Selector selector = null;ServerSocketChannel servChannel = null;try {selector = Selector.open();servChannel = ServerSocketChannel.open();servChannel.configureBlocking(false);servChannel.socket().bind(new InetSocketAddress(port), 1024);servChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器在8001端口守候");} catch (IOException e) {e.printStackTrace();System.exit(1);}while(true){try {selector.select(1000);Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();SelectionKey key = null;while (it.hasNext()) {key = it.next();it.remove();try {handleInput(selector,key);} catch (Exception e) {if (key != null) {key.cancel();if (key.channel() != null)key.channel().close();}}}} catch(Exception ex){ex.printStackTrace();               }try{Thread.sleep(500);}catch(Exception ex){ex.printStackTrace();               }}}public static void handleInput(Selector selector, SelectionKey key) throws IOException {
    ​if (key.isValid()) {// 处理新接入的请求消息if (key.isAcceptable()) {// Accept the new connectionServerSocketChannel ssc = (ServerSocketChannel) key.channel();SocketChannel sc = ssc.accept();sc.configureBlocking(false);// Add the new connection to the selectorsc.register(selector, SelectionKey.OP_READ);}if (key.isReadable()) {// Read the dataSocketChannel sc = (SocketChannel) key.channel();ByteBuffer readBuffer = ByteBuffer.allocate(1024);int readBytes = sc.read(readBuffer);if (readBytes > 0) {readBuffer.flip();byte[] bytes = new byte[readBuffer.remaining()];readBuffer.get(bytes);String request = new String(bytes, "UTF-8"); //接收到的输入System.out.println("client said: " + request);String response = request + " 666";doWrite(sc, response);} else if (readBytes < 0) {// 对端链路关闭key.cancel();sc.close();} else; // 读到0字节,忽略}}}
    ​public static void doWrite(SocketChannel channel, String response) throws IOException {if (response != null && response.trim().length() > 0) {byte[] bytes = response.getBytes();ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);writeBuffer.put(bytes);writeBuffer.flip();channel.write(writeBuffer);}}
    }
    ​
  • package nio;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    import java.util.UUID;
    ​
    public class NioClient {
    ​public static void main(String[] args) {
    ​String host = "127.0.0.1";int port = 8001;
    ​Selector selector = null;SocketChannel socketChannel = null;
    ​try {selector = Selector.open();socketChannel = SocketChannel.open();socketChannel.configureBlocking(false); // 非阻塞
    ​// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答if (socketChannel.connect(new InetSocketAddress(host, port))) {socketChannel.register(selector, SelectionKey.OP_READ);doWrite(socketChannel);} else {socketChannel.register(selector, SelectionKey.OP_CONNECT);}
    ​} catch (IOException e) {e.printStackTrace();System.exit(1);}
    ​while (true) {try {selector.select(1000);Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();SelectionKey key = null;while (it.hasNext()) {key = it.next();it.remove();try {//处理每一个channelhandleInput(selector, key);} catch (Exception e) {if (key != null) {key.cancel();if (key.channel() != null)key.channel().close();}}}} catch (Exception e) {e.printStackTrace();}}​// 多路复用器关闭后,所有注册在上面的Channel资源都会被自动去注册并关闭
    //      if (selector != null)
    //          try {
    //              selector.close();
    //          } catch (IOException e) {
    //              e.printStackTrace();
    //          }
    //
    //      }}
    ​public static void doWrite(SocketChannel sc) throws IOException {byte[] str = UUID.randomUUID().toString().getBytes();ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);writeBuffer.put(str);writeBuffer.flip();sc.write(writeBuffer);}
    ​public static void handleInput(Selector selector, SelectionKey key) throws Exception {
    ​if (key.isValid()) {// 判断是否连接成功SocketChannel sc = (SocketChannel) key.channel();if (key.isConnectable()) {if (sc.finishConnect()) {sc.register(selector, SelectionKey.OP_READ);                    }               }if (key.isReadable()) {ByteBuffer readBuffer = ByteBuffer.allocate(1024);int readBytes = sc.read(readBuffer);if (readBytes > 0) {readBuffer.flip();byte[] bytes = new byte[readBuffer.remaining()];readBuffer.get(bytes);String body = new String(bytes, "UTF-8");System.out.println("Server said : " + body);} else if (readBytes < 0) {// 对端链路关闭key.cancel();sc.close();} else; // 读到0字节,忽略}Thread.sleep(3000);doWrite(sc);}}
    }
    ​

2:Java AIO编程

  • Asynchronous I/O,异步I/O

  • 主要类:AsynchronousServerSocketChannel服务器接受请求通道(bind绑定在某一个端口accept: 接受客户端请求)

  • AsynchronousSocketChannel Socket通讯通道(read 读数据write写数据)

  • CompletionHandler异步处理类(completed操作完成后异步调用方法failed 操作失败后异步调用方法)

  • package aio;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.channels.AsynchronousChannelGroup;
    import java.nio.channels.AsynchronousServerSocketChannel;
    import java.nio.channels.AsynchronousSocketChannel;
    import java.nio.channels.CompletionHandler;
    import java.nio.charset.Charset;
    import java.nio.charset.CharsetDecoder;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    ​
    public class AioServer {
    ​public static void main(String[] args) throws IOException {  AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();   server.bind(new InetSocketAddress("localhost", 8001));  System.out.println("服务器在8001端口守候");//开始等待客户端连接,一旦有连接,做26行任务server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {  @Override  public void completed(AsynchronousSocketChannel channel, Object attachment) {  server.accept(null, this); //持续接收新的客户端请求ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间//开始读取客户端内容,一旦读取结束,做33行任务channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result_num, ByteBuffer attachment) {attachment.flip(); //反转此Buffer CharBuffer charBuffer = CharBuffer.allocate(1024);CharsetDecoder decoder = Charset.defaultCharset().newDecoder();decoder.decode(attachment,charBuffer,false);charBuffer.flip();String data = new String(charBuffer.array(),0, charBuffer.limit());System.out.println("client said: " + data);channel.write(ByteBuffer.wrap((data + " 666").getBytes())); //返回结果给客户端try{channel.close();}catch (Exception e){e.printStackTrace();}}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println("read error "+exc.getMessage());}});​}  @Override  public void failed(Throwable exc, Object attachment) {  System.out.print("failed: " + exc.getMessage());  }  });
    ​while(true){try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
    }
    ​
  • package aio;
    ​
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.channels.AsynchronousSocketChannel;
    import java.nio.channels.CompletionHandler;
    import java.nio.charset.Charset;
    import java.nio.charset.CharsetDecoder;
    import java.util.UUID;
    ​
    ​
    public class AioClient {
    ​public static void main(String[] a) {try{AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();//18行连接成功后,自动做20行任务channel.connect(new InetSocketAddress("localhost", 8001), null, new CompletionHandler<Void, Void>() {
    ​public void completed(Void result, Void attachment) {String str = UUID.randomUUID().toString();//24行向服务器写数据成功后,自动做28行任务channel.write(ByteBuffer.wrap(str.getBytes()), null,new CompletionHandler<Integer, Object>() {
    ​@Overridepublic void completed(Integer result, Object attachment) {try {System.out.println("write " + str + ", and wait response");//等待服务器响应ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间//开始读取服务器反馈内容,一旦读取结束,做39行任务channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result_num, ByteBuffer attachment) {attachment.flip(); //反转此Buffer CharBuffer charBuffer = CharBuffer.allocate(1024 CharsetDecoder decoder = Charset.defaultCharset().newDecoder();decoder.decode(attachment,charBuffer,false);charBuffer.flip();               String data = new String(charBuffer.array(),0, charBuffer.limit());System.out.println("server said: " + data);try{channel.close();}catch (Exception e){e.printStackTrace();}}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println("read error "+exc.getMessage());}});channel.close();} catch (Exception e) {e.printStackTrace();}}
    ​@Overridepublic void failed(Throwable exc, Object attachment) {System.out.println("write error");}
    ​});}
    ​public void failed(Throwable exc, Void attachment) {System.out.println("fail");}
    ​});Thread.sleep(10000);}catch (Exception e) {e.printStackTrace();}}
    }

3:Java Netty编程

  • 关进技术:1.事件 2.事件处理ChannelHandler 3.ChannelHandler工作模式(责任链) 4.ByteBuf(强大的字节容器,提供丰富的API进行操作)

  • package netty1;
    ​
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    ​
    import java.net.InetSocketAddress;
    ​
    public class EchoServer {public static void main(String[] args) throws Exception {int port = 8001;final EchoServerHandler serverHandler = new EchoServerHandler();EventLoopGroup group = new NioEventLoopGroup();try {//ServerBootstrap是netty中的一个服务器引导类ServerBootstrap b = new ServerBootstrap();b.group(group).channel(NioServerSocketChannel.class)  //设置通道类型.localAddress(new InetSocketAddress(port))  //设置监听端口.childHandler(new ChannelInitializer<SocketChannel>() { //初始化责任链@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(serverHandler); //添加处理类}});
    ​ChannelFuture f = b.bind().sync();  //开启监听if(f.isSuccess()){System.out.println(EchoServer.class.getName() +" started and listening for connections on " + f.channel().localAddress());}f.channel().closeFuture().sync();} finally {group.shutdownGracefully().sync();}}
    }
    ​
  • package netty1;
    ​
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    ​
    import java.net.InetSocketAddress;
    ​
    public class EchoClient {public static void main(String[] args) throws Exception {String host = "localhost";int port = 8001;EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port)).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch)throws Exception {ch.pipeline().addLast(new EchoClientHandler());}});ChannelFuture f = b.connect().sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully().sync();}}
    }

4:邮件基础知识

  • 邮件格式:RFC822邮件格式、MIME(图片、音频、视频等)

  • 邮件编码:纯英文(ASCII)

5:Java Mail编程

  • Java邮件的收发方法


第七章 数据库编程

1:数据库和SQL

  • DB:Database=Data+Base

  • DBMS:Database Management System

  • DB种类:Mysql/Oracle/SQL Server

2:SQL语句

  • create table t1( a int,b varchar(20) );

  • insert into t1(a,b) values (1,'abc');

  • select a from t1;

  • select a,b from t1 where a>1;

  • delete from t1 where a=10 and b='ab';

  • update t1 set a= 2, b='cd' where a=1 and b='ab';

  • drop table t1;

3:JDBC

  • Java和数据库是两套平行的系统需要用JDBC连接

  • java.sql.* 和javax.sql.*包,这两个包知识接口类

  • 要根据数据库的版本导入jar包

  • 连接数据库:jabc:mysql://localhost:3306/数据库名称?useSSL=false

  • 连接步骤

  • 注册驱动:class.forName("com.mysql.jdbc.Driver");

  • 创建连接:Connection conn=DriverManager.getConnection(url,user,password);

  • 执行者:Statement stmt = conn.createStatement();

  • 结果集:ResultSet rs =stmt.executeQuery();

  • 释放连接:conn.close

  • import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    ​
    public class JDBCtest1 {public static void main(String[] args) throws Exception {Class.forName("com.mysql.jdbc.Driver");
    ​Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?useSSL=false","root","mysql");
    ​String sql="update account set age = 21 where id = 2";
    ​Statement stmt = conn.createStatement();
    ​int count= stmt.executeUpdate(sql);
    ​System.out.println(count);
    ​stmt.close();
    ​conn.close();}
    }
    ​

4:JDBC高级操作

  • Java提供PrerareStatement,更为安全的执行SQL

  • 和statement区别的是使用“?”来代替字符串的拼接

  • 使用setXXX(int ,Object)的函数来实现对“?”的替换

  • String sql="select*from user where username = ? and password = ?";//执行sql对象//stmt = conn.createStatement();pstm=  conn.prepareStatement(sql);//使用prepareStatement防止sql注入pstm.setString(1,username);//第一个参数表示,第几个问号(从一开始)pstm.setString(2,password);
  • PrerareStatement的好处:防止注入攻击,防止繁琐的字符串拼接,直接设置对象而不需要转换为字符串,使用预编译速度相对Statement快很多

5:数据库连接池

  • 常用的数据库连接池CP30,Druid

  • 理解POOL的概念:初始数、最大数、增量、超时间等参数

  • <c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/db3?useSSL=false</property><property name="user">root</property><property name="password">mysql</property>
    ​<property name="initialPoolSize">5</property>
    ​<property name="maxPoolSize">10</property>
    ​<property name="checkoutTimeout">3000</property></default-config>
    ​
    </c3p0-config>
    ​
    -----------------------------------------------------------------------------------------------
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    ​
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    ​
    public class C3P0 {public static void main(String[] args) throws SQLException {//创建数据库连接池对象DataSource ds=new ComboPooledDataSource();//获取连接对象Connection conn=ds.getConnection();//打印System.out.println(conn);}
    ​
    }
  • druid.propertiesurl=jdbc:mysql://localhost:3306/db3?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
    username=root
    password=mysql
    driver=com.mysql.cj.jdbc.Driver
    initialSize=5
    maxActive=10
    maxWait=3000
    --------------------------------------------------------------------------------------------------
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    ​
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    ​
    public class Druidtest {public static void main(String[] args) throws Exception {Properties pro = new Properties();InputStream is = Druidtest.class.getClassLoader().getResourceAsStream("druid.properties");pro.load(is);DataSource ds = DruidDataSourceFactory.createDataSource(pro);Connection conn = ds.getConnection();System.out.println(conn);}
    }

第八章 Java混合编程

1:Java调用Java程序(RMI)

  • RMI:远程方法调用

  • RMI的参数和返回值:(自动化)传递远程对象(实现Remote接口)、(自动化)传递可序列化对象(实现Serializable接口)

  • RMI优点:跨平台分布式对象调用、完全对象支持、安全策略

  • RMI缺点:双方必须是Java语言实现、不如消息传递协议方便

  • package warehouse1;
    import java.rmi.*;
    ​
    public interface Warehouse extends Remote
    {  double getPrice(String description) throws RemoteException;
    }
    ​
    package warehouse1;
    import java.rmi.*;
    import java.rmi.server.*;
    import java.util.*;
    ​
    ​
    public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
    {private Map<String, Double> prices;
    ​public WarehouseImpl() throws RemoteException{//物品列表prices = new HashMap<>();prices.put("面包机", 24.95);prices.put("微波炉", 49.95);}
    ​public double getPrice(String description) throws RemoteException{Double price = prices.get(description);return price == null ? 0 : price;}
    }
    ​
    package warehouse1;
    import java.net.MalformedURLException;
    import java.rmi.*;
    import java.rmi.registry.LocateRegistry;
    ​
    import javax.naming.*;
    ​
    /*** 产生WarehouseImpl对象,并进行注册在8001端口,对外提供服务* */
    public class WarehouseServer
    {public static void main(String[] args) throws Exception{System.out.println("产生服务器对象");WarehouseImpl centralWarehouse = new WarehouseImpl();
    ​System.out.println("将服务器对象绑定在8001端口,对外提供服务");LocateRegistry.createRegistry(8001);//定义端口号Naming.rebind("rmi://127.0.0.1:8001/warehouse1", centralWarehouse);
    ​System.out.println("等待客户端的调用");}
    }
    ​

4:Java调用C程序(JNI)

  • JNI:Java Native Interface

  • java和本地C代码进行相互操作

  • 采用JNI将丧失跨平台性

5:Java调用JavaScript程序(Nashorn)

  • 通过Nashorm的脚本引擎(ScriptEngine)解析JS

6:Java调用Python程序(Jython)

  • Java通过Jython可以执行python代码

  • Python代码都会被解释为Java字节码进行执行

7:Java调用Web Service

  • Java提供wsimport工具

8:Java调用命令行

  • Java提供Runtime类

  • exec以一个独立进程执行命令command,并返回Process句柄

Java核心技术(进阶)相关推荐

  1. Java基础学习笔记(二)_Java核心技术(进阶)

    本篇文章的学习资源来自Java学习视频教程:Java核心技术(进阶)_华东师范大学_中国大学MOOC(慕课) 本篇文章的学习笔记即是对Java核心技术课程的总结,也是对自己学习的总结 文章目录 Jav ...

  2. Java技术进阶推荐书单

    1.启蒙篇 首先推荐的两本书是Java核心技术,这一套书是获得了第十三届Jolt生成效率大奖,大学的时候几乎是计算机学院的学生爱好Java编程的都会买这两本书进行扫盲.而且这两本书会随着JDK的版本迭 ...

  3. Alibaba大牛常读的10本Java实战书籍,(Java开发进阶必备书单),可以白嫖了

    关乎于程序员,除了做项目来提高自身的技术,还有一种提升自己的专业技能就是:多!看!书! 毕竟,书是学习的海洋呢!So,Java程序员你们准备好了吗?双手奉上Java程序员必读之热门书单. 在下面这 1 ...

  4. 阿里大人都在读的10本Java实战书籍,Java开发进阶必备书单

    关乎于程序员,除了做项目来提高自身的技术,还有一种提升自己的专业技能就是:多!看!书! 毕竟,书是学习的海洋呢!So,Java程序员你们准备好了吗?双手奉上Java程序员必读之热门书单. 在下面这 1 ...

  5. 重磅!两万字长文总结,梳理 Java 入门进阶哪些事(推荐收藏)

    作者 l 程序员小跃 来源 l 程序员小跃(ID:runningdimple) 大家好,我是程序员小跃,一名在职场已经写了 6 年程序的老程序员,从一开始的菊厂 Android 开发到现在某游戏公司的 ...

  6. 两万字长文总结,梳理 Java 入门进阶那些事(推荐收藏)

    大家好,我是程序员小跃,一名在职场已经写了6年程序的老程序员,从一开始的菊厂 Android 开发到现在某游戏公司的Java后端架构,对Java还是相对了解的挺多. 大概是半年前吧,在知乎上有个知友私 ...

  7. 两万字长文总结,梳理 Java 入门进阶那些事

    两万字长文总结,梳理 Java 入门进阶那些事 先给大家看下完整的思维导图,也是这篇文章的主要脉络. Java从入门到进阶学习路线 主导三个项目,让我独当一面 能力提升你要怎么学 全篇总结 Java ...

  8. 两万字梳理 Java 入门进阶那些事

    大家好,我是石头哥,今天跟大家分享一篇 Java 入门到进阶的文章,配合之前我写的[经验分享 -- 程序员编程如何入门.进阶]一起食用更佳哦. 作者是一名在职场已经写了6年程序的老程序员,从一开始的菊 ...

  9. 两万字长文总结,梳理 Java 入门进阶哪些事

    作者 l 程序员小跃 来源 l 程序员小跃(ID:runningdimple) 以下内容中的"小跃",并非指小生本人,而是原作者 大家好,我是程序员小跃,一名在职场已经写了6年程序 ...

  10. 两万字长文总结,梳理 Java 入门进阶哪些事(推荐收藏)

    两万字长文总结,梳理 Java 入门进阶哪些事(推荐收藏) 程序员小跃 2021-01-12 13:19:09  23  收藏 分类专栏: Java学习之路 文章标签: java 数据库 redis ...

最新文章

  1. Asp.net中多项目共享Session
  2. android地图获取坐标位置,android 百度地图 根据得到的经纬度 获取位置信息
  3. 将shell脚本转为python_shell脚本将python脚本加入Linux系统服务
  4. 网络营销——专业的站内、站外优化还是得靠专业网络营销公司
  5. IIS 6.0支持.SHTML
  6. DFS分布式文件系统--管理篇
  7. 【Mybatis-Plus】(四)分页、乐观锁插件 通用枚举 多数据源
  8. 【OpenCV 例程200篇】30. 图像的缩放(cv2.resize)
  9. LeetCode--88.合并两个有序数组(插入法,排序法)
  10. 五天学redhat系列之---安装篇(下)
  11. 积累分布用例-洛伦兹曲线和随机游走
  12. office韩文版本
  13. 计算机桌面黑屏有鼠标,电脑黑屏只有鼠标怎么办
  14. 使用Frodo,在Android中调试RxJava
  15. 程序猿生存指南-41 冬日归乡
  16. 计算机组成原理-存储器的层次结构
  17. 关于pr文件导入的问题
  18. maven项目的pom.xml文件添加依赖
  19. H5实现九宫格效果抽奖
  20. [附源码]Python计算机毕业设计SSM流浪动物救助及领养平台(程序+LW)

热门文章

  1. prettier工具格式化
  2. 从零开始写RISC-V处理器
  3. 【第二剑-构建活动】代码应该怎么写?
  4. 代码写的太烂了,所以我干不下去了
  5. 2021年最后一天,学点Selenium玩点新鲜~新的一年,让分布式测试有更多玩法
  6. 【计算机组成原理】中央处理器总结——基本知识要点汇总
  7. ubuntu18.04下载安装mysql 5.7 【压缩包】
  8. 穿越六年艰难转型,明道云终于再获主流投资
  9. 使用antd-design-vue配合vue框架搭建项目使用组件显示英文的解决办法
  10. 网络时代的平民教育家-- Salman Khan