本篇文章的学习资源来自Java学习视频教程:Java核心技术(进阶)_华东师范大学_中国大学MOOC(慕课)
本篇文章的学习笔记即是对Java核心技术课程的总结,也是对自己学习的总结
Java核心技术(进阶)内容:应用
Java核心技术进阶主要内容:
1、项目构建生命周期:
[清理]–>[编译]–>[测试]–>[报告]–>[打包]–>[安装]–>[发布]
[clean]–>[compile]–>[test]–>[report]–>[package]–>[install]–>[publish]
2、项目中jar包管理
Maven工作原理:
[本地仓库]–>[局域网私服仓库]–>[中央仓库]
3、Maven项目信息管理
安装 解压即可
Maven插件结构:
bin
boot 插件
conf 配置文件
lib
IDEA自带Maven位于D:\DevTools\ideaIU-2020.2.3.win\plugins\maven\lib\maven3目录下,
查看版本号:在maven3/bin目录下,调出cmd,输入mvn -v
所以Maven原本是不需要配置的。只是为了在任何路径下都可以使用Maven命令,所以需要配置
配置
本地仓库默认位置:
C:\Users\Administrator\.m2\repository
重新设置Maven本地仓库地址
Maven安装路径\conf\setting.xml中定位标签
#53 /path/to/local/repository
重新设置本地仓库位置
D:\DevTools\ideaIU-2020.2.3.win\plugins\maven\repository
阿里云镜像设置
在mirror位置(大约160行左右)添加阿里云镜像
alimaven
aliyun maven
http://maven.aliyun.com/nexus/content/groups/public/
central
Maven可以管理的工程,必须按照【约定结构】来创建
结构
src:(Java代码)
main文件夹:主要开发 业务程序
java(创建Java文件)
Java文件创建package,如 src\main\java\com\bjpowernode\model\Student.java
resources(存放程序资源文件(配置文件,properties))
test:主要进行测试 单元测试程序
Java(创建测试类)
Java文件创建测试类,如 src\main\java\com\bjpowernode\model\TestMain.java
resources(存放测试程序资源文件(测试时使用的配置文件))
target文件夹:(编译后class文件,在创建项目时,不需要创建。Maven命令在执行时自动创建target)
pom.xml:(核心配置文件:主要用来向Maven讨要jar包)(Project Object Model)
eg:记事本开发Maven工程
Student.java
package com.bjpowernode.model;
public class Student{
public void sayHello(){
System.out.print("Hello Maven");
}
}
TestMain.java
package com.bjpowernode.test;
import com.bjpowernode.model.Student;
import org.junit.Test;
public class TestMain{
@Test
public void myTest (){
Student stu=n ew Student();
stu.sayHello();
}
}
pom.xml
包含了项目信息,依赖信息、构建信息
4.0.0 //固定不变的
//这三行是定位package的,简称gav
com.bjpowernode //域名
Maven_Project //项目名,不能有中文
6.0 //版本号
//war //这一行通常为默认jar或war,需要时手动添加
//添加依赖
//所需的依赖可以到百度Maven Repository,搜索相关包名,获取gav
junit
junit
4.12
test
1、mvn clean 删除当前工程中target
2、mvn compile 将当前工程中main文件下的所有java编译为class,输送到target文件中
3、mvn test 运行test文件下所有测试文件
4、mvn package 首先将test文件下所有的java测试类的方法调用执行进行测试,并生成[测试报告]。如果测试没有问题,将main文件下所有class文件打成(jar/war),然后输送到target
5、mvn install 与package命令执行基本一致,将[jar\war]推送到Maven的本地仓库
为了方便Maven对jar的定位,在Maven世界中,每一个jar都要由一个独立坐标,相当于ip
artifact:构建
这个独立坐标由三部分组成
软件测试分类
目前JUnit包含JUnit3,JUnit4,JUnit5,主流使用JUnit4
每一个测试方法的头部加@Test(注解),这样JUnit会自动执行这些测试方法
eg:Triangle
package com.ouc.test;
import static org.junit.Assert.*;//导入Assert类的所有静态方法,自JDK1.5引入
import com.ouc.model.Triangle;
import org.junit.Assert;
import org.junit.Test;
public class TestTriangle {
@Test
public void test(){
assertEquals(false,new Triangle().JudgeEdges(1,2,3));//静态assert方法
//Assert.assertEquals(false,new Triangle().JudgeEdges(1,2,3));//动态assert方法
}
}
JUnit单元测试与Maven测试的区别:
JUnit单元测试一次只能测试一个JUnit test,Maven将test文件夹下测试所有test类
编码方案
ANSI编码
Java的字符编码
Java是第一个设计成支持国际化的编程语言
Locale(zh_CN,en_US,…)
Locale方法
语言文件
ResourceBundle类
eg:
创建txt文件,内容:hello=你好,世界
调用jdk下的native2ascii.exe转码,cmd命令行下输入
native2ascii.exe a.txt message_zh_CN.properties
将message_zh_CN.properties放在main\resources下,
public static void main(String[] args) {
//取得系统默认的国家/语言环境
Locale myLocale = Locale.getDefault();
//myLocale=new Locale("en","US");//强制转化en_US
System.out.println("myLocale = " + myLocale);//zh_CN //en_US
//根据指定语言_国家环境加载资源文件
ResourceBundle bundle = ResourceBundle.getBundle("message", myLocale);
System.out.println("bundle.getString(\"hello\") = " + bundle.getString("hello"));
}
正则表达式(REGEX)(Regular Express)
作用
正则表达式就是由普通字符以及特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式用来描述字符串的特征。
正则表达式是非常复杂的,理解正则表达式能做什么(字符串匹配、字符串提取、字符串替换),掌握常用的正则表达式用法。
3个重要的正则表达式命令
在正则表达式中有三种类型的括号,[]、{}、()。中括号[]内是需要匹配的字符,大括号{}内是指定匹配字符的数量,小括号()则是用来改变优先级或分组的
插入符号"^"表示正则式的开始
美元符号"$"表示正则式的结束
eg:Regex obj = new Regex("[a-z]{10}]");//搜索长度为10的a-z的英文字母
. 除\n以外的任意的单个字符 注:有且只有一个
[] 字符组:[]表示在字符组中罗列出来的字符,任意取单个。 eg:a[a-zA-Z王]b -表示范围,-在第一位时只表示一个字符,不表示元字符
以下是通配符:
| 表示“或”的意思 正则表达式尽量减少“|”的使用,“|”越多,越消耗性能
* 表示限定前面的表达式出现0次或者多次
+ 表示一次或多次,至少一次
? 表示出现0次或1次
{} 指定匹配字符的数量 eg:[0-9]{8} [a-z]{4,}
[0-9] \d \D取余
[a-zA-Z0-9_] \w \W取余
\s 表示所有不可见字符
注:要想完全匹配,必须要加^和$
java.util.regex包
eg
private static final String REGEX = "\\bdog\\b";//\b表示边界
private static final String INPUT = "dog dog dog doggie dogg";
public static void main(String[] args) {
//检查字符串里面有多少个dog
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT);
int count = 0;
while (m.find()) {
count++;
System.out.println("count = " + count);
System.out.println("m = " + m.start());
System.out.println("m.end() = " + m.end());
}
}
private static final String REGEX = "Foo";
private static final String INPUT = "Foooooooo";
private static Pattern pattern;
private static Matcher matcher;
public static void main(String[] args) {
//Initialize
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
System.out.println("REGEX = " + REGEX);
System.out.println("INPUT = " + INPUT);
System.out.println("matcher.lookingAt() = " + matcher.lookingAt());//部分匹配
System.out.println("matcher.matches() = " + matcher.matches());//完全匹配
}
private static String REGEX = "a*b";//*表示限定前面的a可以有0或多个
private static String INPUT = "aabfooaabfooabfoobcdd";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern pattern = Pattern.compile(REGEX);
Matcher matcher = pattern.matcher(INPUT);
StringBuffer stringBuffer = new StringBuffer();
//全部替换
while (matcher.find()) {
matcher.appendReplacement(stringBuffer, REPLACE);
}
//将最后的尾巴字符串附加上
matcher.appendTail(stringBuffer);
System.out.println("stringBuffer.toString() = " + stringBuffer.toString());
}
private static String REGEX="dog";
private static String INPUT="The dog says meow.All dogs say meow.";
private static String REPLACE="cat";
public static void main(String[] args) {
Pattern pattern=Pattern.compile(REGEX);
Matcher matcher=pattern.matcher(INPUT);
INPUT=matcher.replaceAll(REPLACE);//将所有的dog换成cat
System.out.println("INPUT = " + INPUT);
}
其他字符串操作
public static void main(String[] args) {
List names = new LinkedList<>();
names.add("xiaohong");
names.add("xiaoming");
names.add("daming");
//从ArrayList变到字符串
String str1=String.join(",",names);
System.out.println("str1 = " + str1);
//JDK自带的String.join方法只能拼接字符串元素
//使用Apache Commons Lang的StringUtils.join方法可以拼接更多类型的元素
String str2= StringUtils.join(names,",");
System.out.println("str2 = " + str2);
//从字符串转到ArrayList
List names2= Arrays.asList(str1.split(","));
for (String name :
names2) {
System.out.println("name = " + name);
}
//StringUtils可以支持更多数据类型
ArrayList ids = new ArrayList<>();
ids.add(1);
ids.add(5);
ids.add(3);
String str3=StringUtils.join(ids,",");
System.out.println("str3 = " + str3);
}
XML:可扩展标记语言
纯文本表示,跨系统/平台/语言
区分大小写
1、文档声明
2、元素=标签
3、属性
4、注释
5、CDATA区、特殊字符
文档声明 文档中必须要有一个根标签
元素=标签
注:xml必须要有根节点,且只能有一个节点
属性
注释
CDATA区、特殊字符
< <
> >
" "
' '
XML Schema(XSD,XML Schema Definition)
XSL
xml解析方法
DOM
eg:
DOMReader:
//自上而下进行访问
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();
System.out.println("usersList.getLength() = " + usersList.getLength()); //1
for (int i = 0; i < usersList.getLength(); i++) {
Node users=usersList.item(i); //1 users
NodeList userList=users.getChildNodes();//获取二级子节点user的列表
System.out.println("userList.getLength() = " + userList.getLength());//9
for (int j = 0; j < userList.getLength(); j++) {
Node user=userList.item(j);
if (user.getNodeType()==Node.ELEMENT_NODE){
NodeList metaList=user.getChildNodes();
System.out.println("metaList.getLength() = " + metaList.getLength());//7
for (int k = 0; k < metaList.getLength(); k++) {
//到最后一级文本
Node meta=metaList.item(k);
if (meta.getNodeType()==Node.ELEMENT_NODE){
System.out.println("metaList.item(k).getTextContent() = " + metaList.item(k).getTextContent());
}
}
}
}
}
System.out.println();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
//根据名字进行搜索
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.getTextContent() = " + element.getTextContent());
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
DOMWriter
public static void main(String[] args) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
//新创建一个Document节点
Document document = builder.newDocument();
if (document != null) {
Element docx = document.createElement("document");
Element element = document.createElement("element");
element.setAttribute("type", "para");
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);
docx.appendChild(element);
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 (ParserConfigurationException | TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
SAX(Simple API for XML)
public class SAXReader {
public static void main(String[] args) throws SAXException, IOException {
XMLReader parser = XMLReaderFactory.createXMLReader();
BoolHandler boolHandler = new BoolHandler();
parser.setContentHandler(boolHandler);
parser.parse("books.xml");
System.out.println("boolHandler = " + boolHandler.getNameList());
}
}
class BoolHandler extends DefaultHandler {
private List nameList;
private boolean title = false;
public List getNameList() {
return nameList;
}
//实现DefaultHandler的5个回调方法
//xml文档加载时
public void startDocument() throws SAXException {
System.out.println("Start parsing document...");
nameList = new ArrayList();
}
//文档解析结束
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 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("bookTitle = " + bookTitle);
nameList.add(bookTitle);
}
}
}
Stax(Streaming API for XML)
eg:
//流模式
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.newFactory();
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();
if (name.equals("title")) {
titleFlag = true;
System.out.println("title:");
}
//取得所有属性
Iterator attrs = start.getAttributes();
while (attrs.hasNext()) {
//打印所有属性信息
Attribute attr = (Attribute) attrs.next();
//System.out.println("attr.getValue() = " + attr.getValue());
}
//如果是正文
if (event.isCharacters()) {
String s = event.asCharacters().getData();
if (null != s && s.trim().length() > 0 && titleFlag) {
System.out.println("s.trim() = " + s.trim());
}
}
//如果事件对象是元素的结束
if (event.isEndElement()) {
EndElement end = event.asEndElement();
String titleName = end.getName().getLocalPart();
if (titleName.equals("title")) {
titleFlag = false;
}
}
}
eventReader.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
JSON
JavaScript Object Notation,js对象表示法
一种轻量级的数据交换格式
JSONObject,名称-值对。如"firstName":“John”
JSONArray,json数组
java的JSON处理
JSON主要用途
eg:
Person.java Java Bean类
public class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List getScores() {
return scores;
}
public void setScores(List scores) {
this.scores = scores;
}
private String name;
private int age;
private List scores;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
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
}
]
}
org.json:
//写JSON文件
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"));
}
//读JSON文件
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 bookList = new ArrayList<>();
for (Object book : books) {
//获取单个JSONObject对象
JSONObject bookObject = (JSONObject) book;
ArrayList book1=new ArrayList();
book1.add(bookObject.getString("author"));
book1.add(bookObject.getString("year"));
book1.add(bookObject.getString("title"));
book1.add(bookObject.getInt("price"));
book1.add(bookObject.getString("category"));
bookList.add(book1);
}
for (ArrayList book : bookList) {
System.out.println(book.get(0)+","+book.get(2));
}
} catch (Exception e) {
e.printStackTrace();
}
}
GSON:
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]
}
public static void testJsonFile() {
Gson gson = new Gson();
File file = new File("books2.json");
try (FileReader reader = new FileReader(file)) {
//从JSON字符串到Java对象
List<Book> books = gson.fromJson(reader, new TypeToken<List<Book>>(){}.getType());
} catch (Exception e) {
e.printStackTrace();
}
}
json和XML比较
图形图像基础概念
Java图形图像关键类
java图像关键类描述
eg:
readAndWriteImage:
public static void readAndWrite() throws Exception {
BufferedImage image = ImageIO.read(new File("c:/temp/ecnu.jpg"));
System.out.println("Height: " + image.getHeight()); // 高度像素
System.out.println("Width: " + image.getWidth()); // 宽度像素
ImageIO.write(image, "png", new File("c:/temp/ecnu.png"));
}
在已知图像格式,选定了该格式的reader,加载速度更快
// 指定用jpg Reader来加载,速度会加快
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = (ImageReader) readers.next();
System.out.println(reader.getClass().getName());
ImageInputStream iis = ImageIO.createImageInputStream(new File("c:/temp/ecnu.jpg"));
reader.setInput(iis, true);
System.out.println("Height:" + reader.getHeight(0));
System.out.println("Width:" + reader.getWidth(0));
裁剪图片
/**
* cropImage 将原始图片文件切割一个矩形,并输出到目标图片文件
* @param fromPath 原始图片
* @param toPath 目标图片
* @param x 坐标起点x
* @param y 坐标起点y
* @param width 矩形宽度
* @param height 矩形高度
* @param readImageFormat 原始文件格式
* @param writeImageFormat 目标文件格式
* @throws Exception
*/
public static void cropImage(String fromPath, String toPath, int x, int y, int width, int height, String readImageFormat,
String writeImageFormat) throws Exception {
FileInputStream fis = null;
ImageInputStream iis = null;
try {
// 读取原始图片文件
fis = new FileInputStream(fromPath);
Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(readImageFormat);
ImageReader reader = it.next();
iis = ImageIO.createImageInputStream(fis);
reader.setInput(iis, true);
// 定义一个矩形 并放入切割参数中
ImageReadParam param = reader.getDefaultReadParam();
Rectangle rect = new Rectangle(x, y, width, height);
param.setSourceRegion(rect);
//从源文件读取一个矩形大小的图像
BufferedImage bi = reader.read(0, param);
//写入到目标文件
ImageIO.write(bi, writeImageFormat, new File(toPath));
} finally {
fis.close();
iis.close();
}
}
拼接图片
/**
* 横向拼接两张图片,并写入到目标文件
* 拼接的本质,就是申请一个大的新空间,然后将原始的图片像素点拷贝到新空间,最后保存
* @param firstPath 第一张图片的路径
* @param secondPath 第二张图片的路径
* @param imageFormat 拼接生成图片的格式
* @param toPath 目标图片的路径
*/
public static void combineImagesHorizontally(String firstPath, String secondPath,String imageFormat, String toPath){
try {
//读取第一张图片
File first = new File(firstPath);
BufferedImage imageOne = ImageIO.read(first);
int width1 = imageOne.getWidth();//图片宽度
int height1 = imageOne.getHeight();//图片高度
//从第一张图片中读取RGB
int[] firstRGB = new int[width1*height1];
firstRGB = imageOne.getRGB(0,0,width1,height1,firstRGB,0,width1);
//对第二张图片做同样的处理
File second = new File(secondPath);
BufferedImage imageTwo = ImageIO.read(second);
int width2 = imageTwo.getWidth();
int height2 = imageTwo.getHeight();
int[] secondRGB = new int[width2*height2];
secondRGB = imageTwo.getRGB(0,0,width2,height2,secondRGB,0,width2);
//生成新图片
int height3 = (height1>height2)?height1:height2; //挑选高度大的,作为目标文件的高度
int width3 = width1 + width2; //宽度,两张图片相加
BufferedImage imageNew = new BufferedImage(width3,height3,BufferedImage.TYPE_INT_RGB);
//设置左半部分的RGB 从(0,0) 开始
imageNew.setRGB(0,0,width1,height1,firstRGB,0,width1);
//设置右半部分的RGB 从(width1, 0) 开始
imageNew.setRGB(width1,0,width2,height2,secondRGB,0,width2);
//保存图片
ImageIO.write(imageNew, imageFormat, new File(toPath));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 纵向拼接图片(两张)
* 拼接的本质,就是申请一个大的新空间,然后将原始的图片像素点拷贝到新空间,最后保存
* @param firstPath 读取的第一张图片
* @param secondPath 读取的第二张图片
* @param imageFormat 图片写入格式
* @param toPath 图片写入路径
*/
public static void combineImagesVertically(String firstPath, String secondPath,String imageFormat, String toPath){
try {
//读取第一张图片
File first = new File(firstPath);
BufferedImage imageOne = ImageIO.read(first);
int width1 = imageOne.getWidth();//图片宽度
int height1 = imageOne.getHeight();//图片高度
//从图片中读取RGB
int[] firstRGB = new int[width1*height1];
firstRGB = imageOne.getRGB(0,0,width1,height1,firstRGB,0,width1);
//对第二张图片做相同的处理
File second = new File(secondPath);
BufferedImage imageTwo = ImageIO.read(second);
int width2 = imageTwo.getWidth();
int height2 = imageTwo.getHeight();
int[] secondRGB = new int[width2*height2];
secondRGB = imageTwo.getRGB(0,0,width2,height2,secondRGB,0,width2);
//生成新图片
int width3 = (width1>width2)?width1:width2; //挑选宽度大的,作为目标文件的宽度
int height3 = height1+height2; //高度,两张图片相加
BufferedImage imageNew = new BufferedImage(width3,height3,BufferedImage.TYPE_INT_RGB);
//设置上半部分的RGB 从(0,0) 开始
imageNew.setRGB(0,0,width1,height1,firstRGB,0,width1);
//设置下半部分的RGB 从(0, height1) 开始
imageNew.setRGB(0,height1,width2,height2,secondRGB,0,width2);
//保存图片
ImageIO.write(imageNew, imageFormat, new File(toPath));
} catch (Exception e) {
e.printStackTrace();
}
}
验证码的生成
验证码,一个图片文件
public class ValidateCodeTest {
//没有1 I L 0 o
static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9' };
static int charNum = codeSequence.length;
public static void main(String[] a) throws IOException
{
generateCode("f:/code.jpg");
}
public static void generateCode(String filePath) throws IOException {
// 首先定义验证码图片框
int width = 80; // 验证码图片的宽度
int height = 32; // 验证码图片的高度
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//定义图片上的图形和干扰线
Graphics2D gd = buffImg.createGraphics();
gd.setColor(Color.LIGHT_GRAY); // 将图像填充为浅灰色
gd.fillRect(0, 0, width, height);
gd.setColor(Color.BLACK); // 画边框。
gd.drawRect(0, 0, width - 1, height - 1);
// 随机产生16条灰色干扰线,使图像中的认证码不易识别
gd.setColor(Color.gray);
// 创建一个随机数生成器类 用于随机产生干扰线
Random random = new Random();
for (int i = 0; i < 16; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
//计算字的位置坐标
int codeCount = 4; // 字符个数
int fontHeight; // 字体高度
int codeX; // 第一个字符的x坐标,因为后面的字符坐标依次递增,所以它们的x轴值是codeX的倍数
int codeY; // 验证字符的y坐标,因为并排所以值一样
// width-4 除去左右多余的位置,使验证码更加集中显示,减得越多越集中。
// codeCount+1 //等比分配显示的宽度,包括左右两边的空格
codeX = (width - 4) / (codeCount + 1); //第一个字母的起始位置
fontHeight = height - 10; // height - 10 高度中间区域显示验证码
codeY = height - 7;
// 创建字体,字体的大小应该根据图片的高度来定。
Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
gd.setFont(font);
// 随机产生codeCount数字的验证码。
for (int i = 0; i < codeCount; i++) {
// 每次随机拿一个字母,赋予随机的颜色
String strRand = String.valueOf(codeSequence[random.nextInt(charNum)]);
int red = random.nextInt(255);
int green = random.nextInt(255);
int blue = random.nextInt(255);
gd.setColor(new Color(red,green,blue));
//把字放到图片上!!!
gd.drawString(strRand, (i + 1) * codeX, codeY);
}
ImageIO.write(buffImg, "jpg", new File(filePath));
}
}
统计图生成
添加依赖
org.jfree
jfreechart
1.0.19
eg:
public class JFreeChartTest {
public static void main(String[] args) {
writeBar("F:/bar.jpg"); // 柱状图
writePie("F:/pie.jpg"); // 饼图
writeLine("F:/line.jpg");// 折线图
}
public static StandardChartTheme getChineseTheme()
{
StandardChartTheme chineseTheme = new StandardChartTheme("CN");
chineseTheme.setExtraLargeFont(new Font("隶书", Font.BOLD, 20));
chineseTheme.setRegularFont(new Font("宋书", Font.PLAIN, 15));
chineseTheme.setLargeFont(new Font("宋书", Font.PLAIN, 15));
return chineseTheme;
}
public static void writeBar(String fileName) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(11, "", "第一季度");
dataset.addValue(41, "", "第二季度");
dataset.addValue(51, "", "第三季度");
dataset.addValue(4, "", "第四季度");
// PlotOrientation.HORIZONTAL横向 PlotOrientation.VERTICAL 竖向
// 引入中文主题样式
ChartFactory.setChartTheme(getChineseTheme());
JFreeChart chart = ChartFactory.createBarChart3D("柱状图", "2018年", "产品总量", dataset, PlotOrientation.VERTICAL,
false, false, false);
try {
ChartUtilities.saveChartAsJPEG(new File(fileName), chart, 600, 300);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void writePie(String fileName) {
DefaultPieDataset pds = new DefaultPieDataset();
pds.setValue("C人数", 100);
pds.setValue("C++人数", 200);
pds.setValue("Java人数", 300);
try {
ChartFactory.setChartTheme(getChineseTheme());
JFreeChart chart = ChartFactory.createPieChart("饼图", pds);
ChartUtilities.saveChartAsJPEG(new File(fileName), chart, 600, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void writeLine(String fileName) {
DefaultCategoryDataset lines = new DefaultCategoryDataset();
//第一条线
lines.addValue(100, "Java核心技术", "1月");
lines.addValue(200, "Java核心技术", "2月");
lines.addValue(400, "Java核心技术", "3月");
lines.addValue(500, "Java核心技术", "4月");
//第二条线
lines.addValue(100, "Java核心技术(进阶)", "1月");
lines.addValue(400, "Java核心技术(进阶)", "2月");
lines.addValue(900, "Java核心技术(进阶)", "3月");
try {
ChartFactory.setChartTheme(getChineseTheme());
JFreeChart chart = ChartFactory.createLineChart("折线图", "时间", "人数", lines);
ChartUtilities.saveChartAsJPEG(new File(fileName), chart, 600, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
}
BarCode和QRCode
Java本身没有处理二维码的库,依赖第三方库
Docx功能和处理
POI
表格文件
第三方包
PDF处理和第三方包
Apache PDFBox
多进程
多线程
多进程VS多线程
Java多线程创建
注:Runnable方式:
class Thread1 extends Thread{
public void run(){
System.out.println("hello");
}
}
class Thread2 implements Runnable{
public void run(){
System.out.println("hello");
}
}
public static void main(String[] a)
{
new Thread1().start();
new Thread(new Thread2()).start();
}
Java多线程启动
启动
Java多线程实现对比
Thread VS Runnable
结论:建议实现Runnable接口来完成多线程
多线程信息共享
通过共享变量在多个线程中共享消息
多线程信息共享问题
eg:
i++,并非原子性操作
变量副本问题的解决方法
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");
}
}
关键步骤加锁限制
eg:
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; // 多个 线程在共享的
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--);
}
}
}
线程状态
多线程管理一些概念
多线程死锁
守护(后台)线程
普通线程的结束,是run方法运行结束
守护线程的结束,是run方法运行结束,或main函数结束
t.setDaemon(true);//设置守护线程
守护线程永远不要访问资源,如文件或数据库等
线程查看工具 jvisualvm
并行困难(任务分配和执行过程高度耦合)
并行模式
java并发编程
线程组ThreadGroup
Executor并发框架
eg:计算1-1000的值
SumTest
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();
}
}
SumTest
public class SumTask implements Callable<Integer> {
//定义每个线程计算的区间
private int startNumber;
private int endNumber;
public SumTask(int startNumber, int endNumber){
this.startNumber=startNumber;
this.endNumber=endNumber;
}
@Override
public 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;
}
}
Fork-Join
eg:
SumTest
//分任务求和
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());
}
}
SumTask
//分任务求和
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;
@Override
protected 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;
}
}
List
Set
Map
Queue&Deque(队列,jdk1.5提出)
粗粒度
Lock
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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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
Barrier
Phaser
Exchanger
Thread/Executor/Fork-Join多线程
定时执行
简单定时器机制
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();
}
}
}
注:一个timer对象可以执行多个计划任务,但是这些任务是串行执行的。如果有一个任务执行很慢,将会影响后续的任务准点运行。
Executor+定时器机制
package schedule;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorTest {
public static void main(String[] a) throws Exception
{
//executeAtFixTime();
//executeFixedRate(); //3s
executeFixedDelay(); //4s
}
public static void executeAtFixTime() throws Exception {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.schedule(
new MyTask(),
1,
TimeUnit.SECONDS);
Thread.sleep(10000);//休眠10秒
executor.shutdown();
}
/**
* 周期任务 固定速率 是以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,
* 如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
* @throws Exception
*/
public static void executeFixedRate() throws Exception {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(
new MyTask(),
1,
3000,
TimeUnit.MILLISECONDS);
Thread.sleep(10000);
executor.shutdown();
}
/**
* 周期任务 固定延时 是以上一个任务结束时开始计时,period时间过去后,立即执行。
* @throws Exception
*/
public static void executeFixedDelay() throws Exception {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleWithFixedDelay(
new MyTask(),
1,
3000,
TimeUnit.MILLISECONDS);
Thread.sleep(10000);
executor.shutdown();
}
}
class MyTask implements Runnable {
public void run() {
System.out.println("时间为:" + new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println("时间为:" + new Date());
}
}
ip
补充:ipv4逐渐枯竭,在NAT网络地址转换和端口加持下坚持下来,即网关和端口
port:端口,0~65535
两台机器通讯就是在IP+Port上进行的
查询ip:Windows:ipconfig Linux/Mac:ifconfig
查询端口:Windows/Linux/Max:netstat -an
网络架构模型
通讯协议:TCP和UDP
Java UDP
eg:
UdpRecv
import java.net.*;
public class UdpRecv
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds=new DatagramSocket(3000);//定义了在本机的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();
}
}
UdpSend
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();
}
}
注意:最好先有receive,再send。不然可能会丢失某个信息
**TCP协议:**有连接、保证可靠的无误差通讯
注:这里所说的服务器不是指硬件服务器,而是软件服务器。软件服务器有两个要求:
Java TCP编程
一些规则:
eg:
TcpClient:
import java.net.*;
import java.io.*;
public class TcpClient {
public static void main(String[] args) {
try {
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8001); //需要服务端先开启
//同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流
InputStream ips = s.getInputStream(); //开启通道的输入流
BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
OutputStream ops = s.getOutputStream(); //开启通道的输出流
DataOutputStream dos = new DataOutputStream(ops);
BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));
while (true)
{
String strWord = brKey.readLine();
if (strWord.equalsIgnoreCase("quit"))
{
break;
}
else
{
System.out.println("I want to send: " + strWord);
dos.writeBytes(strWord + System.getProperty("line.separator"));
System.out.println("Server said: " + brNet.readLine());
}
}
dos.close();
brNet.close();
brKey.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
TcpServer:(只能实现一个client的一次对话)
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();
}
}
}
TcpServer:(可以实现多个client的多次对话)
问题:
TcpServer2
import java.net.*;
public class TcpServer2
{
public static void main(String [] args)
{
try
{
ServerSocket ss=new ServerSocket(8001);
while(true)
{
Socket s=ss.accept();
System.out.println("来了一个client");
new Thread(new Worker(s)).start();
}
//ss.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Worker
import java.net.*;
import java.io.*;
class Worker implements Runnable {
Socket s;
public Worker(Socket s) {
this.s = s;
}
public void run() {
try {
System.out.println("服务人员已经启动");
InputStream ips = s.getInputStream();
OutputStream ops = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ips));
DataOutputStream dos = new DataOutputStream(ops);
while (true) {
String strWord = br.readLine();
System.out.println("client said:" + strWord +":" + strWord.length());
if (strWord.equalsIgnoreCase("quit"))
break;
String strEcho = strWord + " 666";
// dos.writeBytes(strWord +"---->"+ strEcho +"\r\n");
System.out.println("server said:" + strWord + "---->" + strEcho);
dos.writeBytes(strWord + "---->" + strEcho + System.getProperty("line.separator"));
}
br.close();
// 关闭包装类,会自动关闭包装类中所包装的底层类。所以不用调用ips.close()
dos.close();
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
HTTP
Java HTTP编程(java.net包)
eg:
URLConnectionGetTest
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();
}
}
}
HttpClient
JDK HTTPClient
Apache HttpComponents的HttpClient
进一步拓展
NIO主要类
Buffer缓冲区,一个可以读写的内存区域
Channel通道
Selector多路选择器(轮询开关)
AIO
三种I/O比较:
**Netty:**集成的第三方网络通讯库
一个非阻塞的客户端-服务器网络通讯框架
基于异步事件驱动模型
简化Java的TCP和UDP编程
支持HTTP/2,SSL等多种协议
支持多种数据格式,如JSON等
关键技术
《Netty实战》,Norman Maurer著,何品 译,人民邮电出版社
《Netty权威指南》,李林锋,电子工业出版社
主要协议(发送端口25,接收端口110)
Java连接数据库操作步骤
Statement
eg:
public class MySQLTest {
//连接信息
private static String url = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8";
private static String userName = "root";
private static String password = "root";
public static void main(String[] args) {
//创建mysql连接
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("注册驱动成功");
} catch (ClassNotFoundException e) {
System.out.println("注册驱动失败");
e.printStackTrace();
return;
}
databaseUpdate("insert into student(id,name,age,sex) values('004','zhangsan',18,'male')");
databaseUpdate("update student set age = 50 where id = '004'");
databaseUpdate("delete from student where id = 003");
databaseSearch();
}
private static void databaseSearch() {
//创建连接
Connection conn = null;
try {
conn = DriverManager.getConnection(url, userName, password);
//构建数据库执行者
Statement statement = conn.createStatement();
System.out.println("创建Statement成功");
//执行SQL语句并返回结果到resultSet
ResultSet resultSet = statement.executeQuery("select id,name,age,sex from student");
System.out.println("查询成功");
//开始遍历ResultSet数据
while (resultSet.next()) {
System.out.println(resultSet.getString(1) + " " + resultSet.getString(2) + " "
+ resultSet.getByte(3) + " " + resultSet.getString(4));
}
resultSet.close();
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if (!(conn == null)) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
private static void databaseUpdate(String sql) {
//创建连接
Connection conn = null;
try {
conn = DriverManager.getConnection(url, userName, password);
//构建数据库执行者
Statement statement = conn.createStatement();
System.out.println("创建Statement成功");
//执行SQL语句并返回结果到resultSet
int result = statement.executeUpdate(sql);
System.out.println("result = " + result);
System.out.println("执行成功");
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if (!(conn == null)) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
注意:
ResultSet不能做多个笛卡尔积连接
例如:
while(rs1.next()){
while(rs2.next()){
//这个循环次数是 rs1条数*rs2条数
}
}
resultset最好不要超过百条,否则极其影响性能
resultset也不是一口气加载所有的select结果数据
JDBC高级操作
事务
JDBC事务
eg:
// 构建Java和数据库之间的桥梁:URL,用户名,密码
conn = DriverManager.getConnection(url, "root", "123456");
conn.setAutoCommit(false);
insertBook(conn, "insert into t_book values(101, 'aaaa', 10)");
insertBook(conn, "insert into t_book values(102, 'bbbb', 10)");
insertBook(conn, "insert into t_book values(103, 'cccc', 10)");
Savepoint phase1 = conn.setSavepoint(); //设置一个保存点
insertBook(conn, "insert into t_book values(104, 'cccc', 10)");
insertBook(conn, "insert into t_book values(105, 'cccc', 10)");
conn.rollback(phase1); //回滚到phase1保存点,即上面2行无效
conn.commit();
PreparedStatement
eg:
String sql = "insert into t_book(bookid,bookname,price) values(?,?,?)";
//构建数据库执行者
PreparedStatement pstmt = conn.prepareStatement(sql);
//执行SQL语句
int bookid = 10;
String bookName = "Effective Java',50);delete from t_book;insert into t_book values(101, 'faked book";
int price = 50;
pstmt.setInt(1, bookid);
pstmt.setString(2, bookName);
pstmt.setInt(3, price);
int result = pstmt.executeUpdate();
pstmt.close();
批量插入数据
String sql = "insert into t_book(bookid,bookname,price) values(?,?,?)";
//构建数据库执行者
PreparedStatement pstmt = conn.prepareStatement(sql);
//执行SQL语句
String bookName = "aaaaaaaaaaaaaaaa";
int price = 50;
for(int i=200;i<210;i++)
{
pstmt.setInt(1, i);
pstmt.setString(2, bookName);
pstmt.setInt(3, price);
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
注:如果有大量的SQL语句,它们结构相同,仅仅差别在具体数值上,那么可以通过addBatch方法进行批量操作。这样会提高性能,减少数据库负担。
ResultSetMetaData
eg:
//获取结果集的元数据
ResultSetMetaData meta = rs.getMetaData();
int cols = meta.getColumnCount();
for(int i=1;i<=cols;i++)
{
System.out.println(meta.getColumnName(i) + "," + meta.getColumnTypeName(i));
}
享元模式
常用的数据库连接池
eg:
//从c3p0获取
//conn = C3p0Factory1.getConnection();
//conn = C3p0Factory2.getConnection();
//从Druid获取
//conn = DruidFactory1.getConnection();
conn = DruidFactory2.getConnection();
C3p0Factory1
public class C3p0Factory1 {
private static ComboPooledDataSource dataSource = null;
public static void init() throws Exception {
dataSource = new ComboPooledDataSource();
dataSource.setDriverClass( "com.mysql.jdbc.Driver" );
dataSource.setJdbcUrl( "jdbc:mysql://localhost:3306/test" );
dataSource.setUser("root");
dataSource.setPassword("123456");
// the settings below are optional -- c3p0 can work with defaults
dataSource.setMinPoolSize(5); //设置最小值
dataSource.setAcquireIncrement(5); //设置增量
dataSource.setMaxPoolSize(20); //设置最大值
// The DataSource dataSource is now a fully configured and usable pooled DataSource
}
public static Connection getConnection() throws Exception {
if(null == dataSource)
{
init();
}
return dataSource.getConnection();
}
}
C3p0Factory2
public class C3p0Factory2 {
private static ComboPooledDataSource dataSource = null;
public static void init() throws Exception {
dataSource = new ComboPooledDataSource();
//dataSource 自动加载c3p0-config.xml文件
// The DataSource dataSource is now a fully configured and usable pooled DataSource
}
public static Connection getConnection() throws Exception {
if(null == dataSource)
{
init();
}
return dataSource.getConnection();
}
}
c3p0-config.xml
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test
root
123456
5
20
DruidFactory1
public class DruidFactory1 {
private static DruidDataSource dataSource = null;
public static void init() throws Exception {
dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
dataSource.setInitialSize(5);
dataSource.setMinIdle(1); //设置idle时间
dataSource.setMaxActive(10); //设置最大连接数
// 启用监控统计功能 dataSource.setFilters("stat");//
}
public static Connection getConnection() throws Exception {
if(null == dataSource)
{
init();
}
return dataSource.getConnection();
}
}
DruidFactory2
public static void init() throws Exception {
Properties properties = new Properties();
InputStream in = DruidFactory2.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(in);
dataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
in.close();
}
druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=123456
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
多虚拟机JVM的程序运行
RMI:Remote Method Invocation 远程方法调用
RMI的参数和返回值
eg:
RMI优点
RMI缺点
JNI
eg:
class HelloNative{
public static native void greeting();
}
Runtime