链接:java开发手册
提取码:kes8
链接:JDK1.8详细文档
提取码:n9zo
两种类型的类声明:
普通类声明
class 类名 {}
枚举声明
enum 类名 {}
PS:如果一个类与其任何包含的类或接口具有相同的简单名称,则是编译时错误。
成员变量(属性),成员方法,构造方法,初始化块,静态块
对象是类的具体实现。一切皆对象。
作用:防止名称冲突,好区分同名类。
package语句必须放在类定义的最前面
public 公开的、共享的、 private 私有的 、 protected 受保护的 、缺省或默认的访问权限
private访问权限:同类
public访问权限:所有人
protected访问权限:同类,同包,子类
默认的访问权限:同类,同包
方法名称跟类名保持一致;
没有定义构造方法时,编译器会自动创建一个默认的无参的构造方法;
构造方法的作用是在new的时候,初始化对象的属性,或者做其他初始化工作;
JVM规范定义:所有的构造方法在编译之后的方法名为<init>,返回的类型为Class类型,只能被JVM调用。
重载的条件:
指向当前对象
this()可以调用本类的重载构造方法,且this()调用必须放在第一行
静态属性:能被所有对象所共享,在内存中只有一个副本,它当且仅当在类的初始化加载时会被初始化。
静态方法:在静态方法中不能访问非静态成员变量和非静态成员方法。
静态代码块:static块可以置于类中的任何地方,类中可以有多个static块。在初始化被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
通过类名.静态方法或静态属性调用。
父类的静态块->子类的静态块->父类的初始化块->父类的构造方法子类的->子类的初始化块->子类的构造方法
静态块:在类的加载期间,做static初始化工作,针对于当前类的静态属性。
初始化块:初始化块在虚拟机编译之后,会生成一个<clinit>方法
所有类的父类都指向Object类
指向当前父类的对象
super()来调用父类的构造方法,且super()调用必须放在第一行
抽象类,不具体、不完整的类。
当一个类定义为抽象类之后,就不能被实例化。
抽象类可以没有抽象方法,有抽象方法的类一定是抽象类。
当一个非抽象子类继承了抽象类就必须对抽象方法进行实现,并且子类的方法和参数必须和父类保持一致。
abstract可以用来修饰方法,代表这个方法为抽象方法,此方法不能有具体的实现。
父类的引用只能调用父类定义的方法
父类的引用如果要调用子类定义的方法,需要将父类的引用强转为子类的类型
强制转换的时候,建议使用instanceof运算符进行判断(防止对象的引用是引用数据类型的特殊值null)
当前类不能被继承
当前方法不能被Override
接口是对行为的规范,抽象类是对数据模板的规范。
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
数组是一个包含固定数量的单一类型值的容器对象。 创建数组时将确定数组的长度
int[] anArray = new int[10];
int[] anArray = { 100, 200, 300};
数组的首地址+i*数组的元素的大小
指的的一个public类的内部定义另外一个类
toString()
返回对象字符串描述,也可以理解为返回对象的值.可以被子类重写。
hashCode()
返回对象的”内存地址”的哈希算法值,可以被子类重写
equals(Object obj)
比较两个对象是否是同一个对象,可以被子类重写。
finalize()
当没有任何引用指向对象时,该对象被销毁前,由java垃圾回收器调用该方法,进行”清理工作”.如:关闭I/0、数据库链接、网络连接等.该方法可以被子类重写。
/**
* 学生类
*/
public class Student {
//学号
private String stuNo;
//姓名
private String name;
public Student() {
}
public Student(String stuNo, String name) {
this.stuNo = stuNo;
this.name = name;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "学号:" + this.stuNo + ",姓名:" + this.name;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Student) {
//强制转换成学生
Student otherStu = (Student) obj;
if (this.stuNo.equals(otherStu.stuNo)) {
return true;
} else {
return false;
}
} else {
//如果是其它类型对象,返回false,表示对象值不相等
return false;
}
}
@Override
public int hashCode() {
return null != this.stuNo ? this.stuNo.hashCode() : 0;
}
/**
* 当没有任何引用指向对象时,该对象被销毁前,由java垃圾回收器调用
* 该方法,进行"清理工作".如:关闭I/0、数据库链接、网络连接等,该方法
* 可以被子类重写
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println(this.name + "即将被销毁...");
}
public static void main(String[] args) {
Student stu = new Student("011", "小明");
System.out.println(stu.toString());
Student stu2 = new Student("012", "小明");
System.out.println(stu2.equals(stu2));
System.out.println(stu2.hashCode());
//获取该对象所有class类型对象
Class stuClass = stu2.getClass();
System.out.println(stuClass);
stu2 = null;
//运行java垃圾回收器
System.gc();
//Runtime.getRuntime().gc();
}
}
String str1 = "abc";
String str2 = new String("abc中");
//获取字符串长度,即字符串中字符的数量
int count = str2.length();
System.out.println(count);
//字符串索引0到长度-1
for (int index = 0; index < count; index++) {
char ch = str2.charAt(index);
System.out.println(ch);
}
//转大小写
str2=str2.toUpperCase();//转大写
System.out.println(str2);
String baidu="https://www.baidu.com";
//字符串截取,获取子字符串
System.out.println(baidu.substring(12,17));
System.out.println(baidu.substring(12));
//字符串替换
baidu="https://www.baidu.com?wd=www.taobao.com";
System.out.println(baidu.replace("www","***"));
//查找子字符串
//查找"https://www.baidu.com?wd=www.taobao.com";是否包含www
System.out.println(baidu.indexOf("www"));
int i=baidu.indexOf('.');
System.out.println(baidu.indexOf("www",i));
System.out.println(baidu.lastIndexOf("www"));
//字符串拼接
//String str3="abc"+1+true;
String str3="abc"+"123";
System.out.println(str3);
String str6="123";
System.out.println(str6.concat("abc"));
//去左右两边空格
String str7=" 12 3 ";
System.out.println(str7.trim());
//字符串分割
String str8="ab;cd;ef";
String [] strs= str8.split(";");
for(String str:strs){
System.out.println(str);
}
//比较两个字符串是否相等
String strA=new String("abc");
String strB=new String("abc");
System.out.println(strA.equals(strB));//区分大小写
System.out.println(strA.equalsIgnoreCase(strB));//不区分大小写
System.out.println(strA==strB);
//将其它对象转换成字符串,如:123->"123"
String str9= String.valueOf(123);
String str = "123";
String str1 = "123";
//equals比较两个字符串是否是同一个字符串
System.out.println(str.equals(str1));
//==比较两个字符串引用是否指向同一个字符串对象
System.out.println(str == str1);
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println(str3.equals(str4));
System.out.println(str3 == str4);
线程非安全的,多线程环境效率高于StringBuffer
线程安全的,同步的.现实方式和使用与StringBuilder相同
public class MyStringBuffer {
private char[] value;
private int length;
public MyStringBuffer(int capacity) {
if (capacity <= 0) {
value = new char[16];
}
value = new char[capacity];
}
public MyStringBuffer(String str) {
this(str.length() + 16);
append(str);
}
public void append(char c) {
/*
* if (value.length < length + 1) { expansion(); } value[length++] = c;
*/
append(new char[] { c });
}
public void append(char[] str) {
if (value.length < length + str.length) {
expansion();
}
System.arraycopy(str, 0, value, this.length, str.length);
this.length = this.length + str.length;
}
public void append(String str) {
/*
* char[] array = str.toCharArray(); if (value.length < array.length + length) {
* expansion(); } System.arraycopy(array, 0, value, this.length, array.length);
* this.length = this.length + str.length();
*/
append(str.toCharArray());
}
public void delete(int start, int end) {
if (start < 0) {
throw new StringIndexOutOfBoundsException(start);
}
if (end > length) {
end = length;
}
if (start > end) {
throw new StringIndexOutOfBoundsException();
}
int len = end - start;
if (len > 0) {
System.arraycopy(value, start + len, value, start, length - end);
length -= len;
}
}
public int indexof(String str) {
/*
* char[] array = str.toCharArray();
* char first = array[0];
* int index = -1;
* for (int i = 0; i < length; i++) {
* if (value[i] != first) { continue; }
* int count = 1;
* for (int j = 1; j < array.length; j++) {
* if ((i + j) >= length) {
* break; }
* if (value[i + j] != array[j])
* { break; }
* count++; } // 条件匹配即退出
* if(count == array.length) {
* index = i; break; } }
* return index;
*/
int index = -1;
char[] array = str.toCharArray();
for (int i = 0; i < length; i++) {
boolean flag = true;
for (int j = 0; j < array.length; j++) {
if (array[j] != value[i + j]) {
flag = false;
break;
}
}
if (flag) {
index = i;
break;
}
}
return index;
}
public int lastIndexof(String str) {
int index = -1;
char[] array = str.toCharArray();
for (int i = length - array.length; i >= 0; i--) {
boolean flag = true;
for (int j = array.length - 1; j >= 0; j--) {
if (array[j] != value[i + j]) {
flag = false;
break;
}
}
if (flag) {
index = i - array.length + 1;
break;
}
}
return index;
}
public void insert(int offset, char[] str) {
if ((offset < 0) || (offset > value.length)) {
throw new StringIndexOutOfBoundsException(offset);
}
if (value.length < length + str.length) {
expansion();
}
// for (int i = length - 1; i >= offset; i--) {
// value[i + str.length] = value[i];
// }
System.arraycopy(value, offset, value, offset + str.length, length - offset);
System.arraycopy(str, 0, value, offset, str.length);
length = length + str.length;
}
public int Length() {
return length;
}
public String subString(int start, int end) {
char[] array = new char[end - start];
System.arraycopy(value, start, array, 0, end - start);
return String.valueOf(array);
}
/**
* 清除左右空格
*/
public void trim() {
char[] array = new char[length];
int begin = 0;
int end = 0;
for (int i = 0; i < length; i++) {
if (!Character.isSpaceChar(value[i])) {
begin = i;
break;
}
}
delete(0, begin);
for (int i = length - 1; i >= 0; i--) {
if (!Character.isSpaceChar(value[i])) {
end = i;
break;
}
}
delete(end + 1, length);
}
public char charAt(int index) {
while (index >= 0 && index < length) {
return value[index];
}
throw new ArrayIndexOutOfBoundsException(index);
}
@Override
public String toString() {
char[] array = new char[length];
System.arraycopy(value, 0, array, 0, length);
return String.valueOf(array);
}
/**
* 扩容
*/
public void expansion() {
char[] bigValue = new char[value.length * 2];
System.arraycopy(value, 0, bigValue, 0, length);
value = bigValue;
}
}
Math类就是用来进行数学计算的,它提供了大量的静态方法来便于我们实现数学计算。
求绝对值:
Math.abs(-100); // 100
Math.abs(-7.8); // 7.8
取最大或最小值:
Math.max(100, 99); // 100
Math.min(1.2, 2.3); // 1.2
计算xy次方:
Math.pow(2, 10); // 2的10次方=1024
计算√x:
Math.sqrt(2); // 1.414...
计算ex次方:
Math.exp(2); // 7.389...
计算以e为底的对数:
Math.log(4); // 1.386...
计算以10为底的对数:
Math.log10(100); // 2
三角函数:
Math.sin(3.14); // 0.00159...Math.cos(3.14); // -0.9999...Math.tan(3.14); // -0.0015...Math.asin(1.0); // 1.57079...Math.acos(1.0); // 0.0
Math还提供了几个数学常量:
double pi = Math.PI; // 3.14159...double e = Math.E; // 2.7182818...Math.sin(Math.PI / 6); // sin(π/6) = 0.5
生成一个随机数x,x的范围是0 <= x < 1
:
Math.random(); // 0.53907... 每次都不一样
自动装箱和自动拆箱
new Integer(2);
Integer k = new Integer(1); int y = k.intValue();
Integer j=1;
int i=new Integer(2);
// getProperty(String)方法使用的当前系统属性集作为一个properties对象返回 Properties properties=System.getProperties(); for (Object key : properties.keySet()) { System.out.println(key+":"+properties.getProperty(String.valueOf(key))); }// System.currentTimeMillis()返回当前时间的毫秒数 System.out.println(System.currentTimeMillis());// System.nanoTime()返回正在运行的Java虚拟机的高精确率时间源的当前值,以纳秒为单位 System.out.println(System.nanoTime());// 终止当前运行的Java虚拟机 System.exit(1);// 运行垃圾收集器 System.gc();// 数组复制,调用系统接口 src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度 System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)// 标准的输出流 System.out.println("12313");
异常是由于程序代码的问题所产生的,当发生异常后,JVM会产生一个异常对象,如果不对异常进行处理,程序会终止执行,JVM会异常退出.java语言每种异常都有个名字,即异常类,所有异常类都继承java.lang.Exception
错误是由硬件设备所导致的,是应用程序不应该试图捕获的严重问题.java语言中每种错误都有个名字,即错误类,所有错误类都继承java.lang.Error.
如:java.lang.StackOverflowError:堆栈溢出错误,用程序递归太深而发生堆栈溢出时,抛出该错误.
public static void main(String[] args) { test();}public static void test() { test();}
如:java.lang.OutOfMemoryError:内存溢出或没有可用的内存时,抛出该错误
public static void main(String[] args) { //Requested array size exceeds VM limit int i[] = new int[0x7fffffff];}
处理(必须要处理)
算术异常:ArithmeticException
空指针异常:NullPointerException
数组索引下标越界异常:ArrayIndexOutOfBoundsException
字符串索引下标越界异常:StringIndexOutOfBoundsException
Class类型转换错误异常:ClassCastException
//算术异常:ArithmeticException//int i=3/0;//空指针异常:NullPointerException//String str=null;//str.length();// int [] i={ };//数组索引下标越界异常:ArrayIndexOutOfBoundsException//System.out.println(i[0]);//String str="abc";//字符串索引下标越界异常:StringIndexOutOfBoundsException//str.charAt(3);//Class类型转换错误异常:ClassCastException//Object ob=new Integer(3);//String str=(String)ob;
try { int [] i={}; System.out.println(i[0]); System.out.println("未发生异常");}catch (NullPointerException e) { System.out.println("发生空指针异常");} catch (ArithmeticException e) { System.out.println("发生算术异常");}finally{ //不管有没有发生异常,都需要执行 System.out.println("finally");}System.out.println("main end");/**只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。--如果代码中执行了if语句那么finally不会执行--如果i/0不被注释那么finally也不会执行--如果主动调用system.exit(0)退出JVM,那么finally也不会执行--finally代码块中使用return那么运行结果一定是finally中的返回值--finally代码块中只赋值不return不影响最终运行结果**/public class FinallyDemo { public static void main(String[] args) { int i = test(); System.out.println(i); } private static int test() { int i=1; // if(i==1){ // return 0; // } // i = i/0; try { System.out.println("try"); System.exit(0); return i; }catch (Exception e){ System.out.println("cache"); return i; }finally { System.out.println("finally"); //return 666; i=666; } } }
public static void method1(String text) throws ParseException, SQLException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD"); Date date = sdf.parse(text); System.out.println(date); DriverManager.getConnection("","","");}public static void method2(String text) throws ParseException, SQLException { method1(text);}public static void main(String[] args) throws ParseException , SQLException{ String text = "2019-09-01"; method2(text); System.out.println("main end");}
代码语法没有错误,但不符合业务逻辑设计.例如: 用int类型表示性别状态,1:男;2:女,当出现1和2意外的数字时,就违反了业务逻辑设计.如果程序继续运行下去,可能会导致更大的业务逻辑错误,这时候,程序员可以手动抛出异常,阻止业务方法继续执行.
* 根据性别状态打印性别名称 * @param sex */public static void showSexName(int sex){ if(1==sex){ System.out.println("男"); }else if(2==sex){ System.out.println("女"); }else{ //手动抛出异常 throw new RuntimeException("性别错误"); }}
在一个大型项目中,可以自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的。
一个常见的做法是自定义一个BaseException
作为“根异常”,然后,派生出各种业务类型的异常。
BaseException
需要从一个适合的Exception
派生,通常建议从RuntimeException
派生:
public class BaseException extends RuntimeException {
}
其他业务类型的异常就可以从BaseException
派生:
public class UserNotFoundException extends BaseException {
}
public class LoginFailedException extends BaseException {
}
...
自定义的BaseException
应该提供多个构造方法:
public class BaseException extends RuntimeException {
public BaseException() {
super();
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(String message) {
super(message);
}
public BaseException(Throwable cause) {
super(cause);
}
}
Date date=new Date();
System.out.println(date);
//获取日期对象的毫秒数
long time= date.getTime();
int year= 1900+ date.getYear();//获取年
System.out.println(year);
int month=date.getMonth()+1;//获取月0-11
System.out.println(month);
//获取周中的天 0-6
int dayInWeek=date.getDay();
System.out.println(dayInWeek);
package cn.zys1314.javase.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
public class CalendarTest {
public static void main(String[] args) throws ParseException {
Calendar ca = Calendar.getInstance();
System.out.println(ca.get(Calendar.YEAR));
// 每个星期的第几天 星期天是第一天
System.out.println(ca.get(Calendar.DAY_OF_WEEK));
System.out.println(ca.get(Calendar.WEEK_OF_MONTH));
System.out.println(ca.get(Calendar.WEEK_OF_YEAR));
ca.set(Calendar.WEEK_OF_YEAR, 1);
System.out.println(ca.get(Calendar.WEEK_OF_YEAR));
System.out.println(ca.get(Calendar.MONTH));
ca.add(Calendar.MONTH, -2);
System.out.println(ca.get(Calendar.MONTH));
System.out.println("------------------------------------------");
// date String
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日hh时mm分ss秒");
Date date = new Date();
System.out.println(sdf.format(date));
String string = "2020年12月19日14时52分30秒";
Date date2 = sdf.parse(string);
System.out.println(date2);
}
}
package cn.zys1314.javase.util;
import java.util.Calendar;
public class PrintDays {
/**根据年、月打印本月所有的日期
* 要点:
* 1.根据年、月确定本月天数
* 2.本月第一天周中的天
* 3.每行打印7个日期,当打印到第一天或最后一天换行
* @param year 年
* @param month 月
*/
public static void printOneMonth(int year, int month) {
Calendar c=Calendar.getInstance();
c.set(year,month-1,1);
//获取本月共有多少天
int days=c.getActualMaximum(Calendar.DAY_OF_MONTH);
//获取本月第1天周中的天
int dayOfWeek=c.get(Calendar.DAY_OF_WEEK);//1-7
System.out.println("日\t一\t二\t三\t四\t五\t六");
//打印本月第1天前面的空格
int b=dayOfWeek-1;
for(int i=1;i<=b;i++){
System.out.print(' ');
System.out.print('\t');
}
//打印本月的所有日期
for(int d=1;d<=days;d++){
System.out.print(d);
System.out.print('\t');
if(7==dayOfWeek){
System.out.println();
dayOfWeek=1;
}else{
dayOfWeek++;
}
}
public static void main(String[] args) {
printOneMonth(2020,12);
}
}
Random random = new Random();
for (int i = 0; i < 8; i++) {
int type = random.nextInt(3);
char ch = '\0';
switch (type) {
case 0:
ch = (char) (random.nextInt(26) + 65);
break;
case 1:
ch = (char) (random.nextInt(26) + 97);
break;
case 2:
ch = (char)(random.nextInt(10)+'0');
break;
default:
break;
}
System.out.print(ch);
}
Java标准库自带的java.util
包提供了集合类:Collection
,它是除Map
外所有其他集合类的根接口。
List 接口的大小可变数组实现.允许包括null,包含重复元素
ArrayList:底层是动态数组、查找快增删慢, 默认初始容量是10、每次采用1.5倍的扩容。
//创建ArrayList对象
ArrayList<String> list = new ArrayList<>();
//添加对象
list.add("abc");//0
list.add("123");//1
list.add("你好");//2
list.add("abc");//3
//获取ArrayList的长度
int count= list.size();
System.out.println(count);
//获取ArrayList中的对象
System.out.println(list.get(1));
//list.remove("abc");
String str=list.remove(1);
System.out.println(str);
System.out.println(list);
//清空,及删除所有的对象
list.clear();
System.out.println(list);
list.add("baidu");
list.add("taobao");
list.add("jd");
//判断是否包含"taobao"
list.contains("taobao");
System.out.println(list.isEmpty());
list.clear();
System.out.println(list.isEmpty());
list.add("zhongshan");//0
list.add("shanghai");
list.add("beijing");
list.add("zhongshan");
list.add(null);
//查找是否包含某个对象
System.out.println(list.indexOf("zhongshan"));
System.out.println(list.lastIndexOf("zhongshan"));
//打印输出或得到ArrayList中所有的对象
int len=list.size();
for(int index=0;index<len;index++){
System.out.println(list.get(index));
}
System.out.println("**************");
//forEach
for(String str1:list){
System.out.println(str1);
}
Vector 实现方式与ArrayList相同,为大小可变的”动态数组”.
Vector与ArrayList区别:
1.Vector是线程同步的,即线程安全的;而Arraylist是线程非安全的.如果不考虑到线程的安全因素,一般用Arraylist效率比较高。
2.如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而Arraylist增长率为目前数组长度的50%.如果在集合中使用数据量比较大的数据,用Vector有一定的优势。
Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法
List 接口的双向链表实现,允许包含null,包含重复元素.此外,LinkedList还实现 Deque 接口,提供先进先出队列操作,以及其他堆栈和双端队列操作
//LinkedList基本使用
List<String> list=new LinkedList<>();
//向链表中添加对象
list.add("abc");
list.add("123");
list.add("tom");
System.out.println(list);
System.out.println( list.get(1));
list.remove(1);
int size=list.size();
//清空
list.clear();
//包含
list.contains("abc");
//双向链表 LinkedList实现栈的特点
LinkedList<String> stack = new LinkedList<>();
//将对象添加到栈中,即压入到栈底
stack.push("123");
stack.push("abc");
stack.push("tom");
stack.push(null);
//查看栈顶的对象或数据
//System.out.println(stack.peek());
while(!stack.isEmpty()){
//移除栈顶的对象
String str=stack.pop();
System.out.println(str);
}
//双向链表 LinkedList实现队列的特点
LinkedList<String> deque = new LinkedList<>();
//将对象或数据加入队列:入队
deque.offer("123");
deque.offer("abc");
deque.offer("tom");
System.out.println(deque);
while(!deque.isEmpty()){
//移除队列中第一个对象 ,即出队
String str=deque.poll();
System.out.println(str);
}
基于哈希表的 Map接口的实现 .提供所有可选的映射操作 , 并允许使用 null 值和 null 键,不保证映射的顺序(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 相同)
底层是数组+链表+红黑树,默认初始容量是16、扩容因子为0.75,每次采用2倍的扩容
//创建哈希表
Map<String, String> userMap = new HashMap<>();
//添加key->value
userMap.put("xiaoming", "小明");
userMap.put("tom", "汤姆");
userMap.put("cat", "猫V1.0");
//根据key获取value
System.out.println(userMap.get("xiaoming"));
//获取键值对数量
int count = userMap.size();
System.out.println(count);
System.out.println(userMap);
userMap.put("cat", "猫V2.0");
System.out.println(userMap);
//删除键值对
userMap.remove("cat");
System.out.println(userMap);
//清空所有键值对
userMap.clear();
System.out.println(userMap);
//判断哈希表是否为空,即不包含任何的键值对
userMap.isEmpty();
userMap.put(null, null);
Map<String, Student> stuMap = new HashMap<>();
stuMap.put("S001", new Student("S001", "学生1", 1, new Date()));
stuMap.put("S002", new Student("S002", "学生1", 1, new Date()));
//stuMap.put("S003", new Student("S003", "学生1", 1, new Date()));
stuMap.put("S004", new Student("S004", "学生1", 1, new Date()));
stuMap.put("S005", new Student("S005", "学生1", 1, new Date()));
Student stu = stuMap.get("S003");
if (stu == null) {
System.out.println("未查找到S003的学生");
} else {
System.out.println(stu);
}
此类实现一个哈希表 ,该哈希表将键映射到相应的值 ,任何非 null 对象都可以用作键或值 .
Hashtable与HashMap的区别
HashMap | HashTable | |
---|---|---|
线程安全 | 非线程安全 | 线程安全 |
底层数据结构 | Object数组+链表+红黑树 | Object数组+链表 |
Null支持 | Key和Value都可以取Null值 | Key不可以取Null值 |
初始容量和扩容 | 默认容量是16,扩容为2N | 默认初始容量是11,扩容为2N+1 |
效率 | 效率高 | 效率低、已经弃用 |
基于红黑树 (Red -Black tree)NavigableMap 实现 .该映射根据其键的自然顺序进行排序或者根据创建映射时提供的 Comparator 进行排序 .
要点:
Set集合的特点就是无序,不可重复,对比List集合是有序的,可以重复。
HashSet:HashSet的底层其实就是一个HashMap,
只是我们add的时候只使用了HashMap的key部分。
TreeSet:可以对Set集合进行排序,默认自然排序。
Set接口常见的实现类有哪些?
TreeSet存放自定义类型如何实现有序的?
分别是采用Comparable接口和Comparator来实现
package cn.zys1314.javase.util;
import java.util.ArrayList;
import java.util.Iterator;
public class IterableTest {
/**
* 1.要获得Iterator对象,就要实现Iterable接口,重写iteator()方法;
* 2.集合调用iterator方法获取Iterator对象,这个迭代器对象是绑定在这个集合对象上
* 3.调用Iterator的hasNext()方法,判断是否还有下一个元素存在
* 4.调用Iterator.next(),获取下一个元素
* 5.迭代器用完之后(Iterator到达集合的末尾),这个迭代器就作废了
* 6.如果需要重新迭代集合,需要重新调用集合的iterator()方法返回对应的Iterator对象
* 7.在迭代的过程中是不能调用Collection的remove()方法来删除元素的;
* 8.如果需要删除元素,需要调用Iteration的默认实现的remove()方法,remove()方法删除的对象是当前Iterator指向的元素
* 9.要实现Iterable接口才能使用for-each-Loop
*/
public static void main(String[] args) {
ArrayList list =new ArrayList();
for (int i = 0; i < 10; i++) {
list.add(i);
}
Iterator iterable=list.iterator();
while (iterable.hasNext()) {
Object object=iterable.next();
if(object.equals(5)) {
//list.remove(object);
iterable.remove();
}
System.out.println(object);
}
System.out.println("===========");
iterable=list.iterator();
while (iterable.hasNext()) {
Object object=iterable.next();
System.out.println(object);
}
}
}
package test.day04;
import java.util.Iterator;
public class MylinkedList implements Iterable {
private int size;
private Node first;
private Node last;
public void addFirst(Object e) {
Node head = first;
Node newNode = new Node(e, null, head);
first = newNode;
if (head == null) {
last = newNode;
} else {
head.prev = newNode;
}
size++;
}
public void addLast(Object e) {
Node tail = last;
Node newNode = new Node(e, tail, null);
last = newNode;
if (last == null) {
first = newNode;
} else {
tail.next = newNode;
}
size++;
}
public void add(int index, Object e) {
if (index < 0 || index > size) {
throw new NullPointerException();
}
if (index == 0) {
addFirst(e);
} else if (index == size) {
addLast(e);
} else {
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
Node newNode = new Node(e, temp.prev, temp);
temp.prev.next = newNode;
temp.prev = newNode;
size++;
}
}
public int indexof(Object o) {
Node node = first;
int count = 0;
while (node!= null) {
if (o.equals(node.value)) {
return count;
}
count++;
node = node.next;
}
return -1;
}
public int lastIndexof(Object o) {
Node node = last;
int count = 0;
while (node!= null) {
count++;
if (o.equals(node.value)) {
return size-count;
}
node = node.prev;
}
return -1;
}
public Object get(int index) {
if (index < 0 || index >= size) {
throw new NullPointerException();
}
Node newNode = first;
for (int i = 0; i < index; i++) {
newNode = newNode.next;
}
return newNode;
}
public void set(int index, Object e) {
if (index < 0 || index >= size) {
throw new NullPointerException();
}
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.value = e;
}
public Object getFirst() {
if (first == null) {
throw new NullPointerException();
}
return first;
}
public Object getLast() {
if (last == null) {
throw new NullPointerException();
}
return last;
}
public Object removeFirst() {
if (first == null) {
throw new NullPointerException();
}
Node newNode = first;
first = newNode.next;
newNode.next.prev = null;
size--;
return newNode;
}
public Object removeLast() {
if (last == null) {
throw new NullPointerException();
}
Node newNode = last;
last = newNode.prev;
last.next = null;
newNode.prev = null;
size--;
return newNode;
}
public Object remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException(index + "");
}
Node newNode = first;
if (index == 0) {
newNode = (Node) removeFirst();
} else if (index == size - 1) {
newNode = (Node) removeLast();
} else {
for (int i = 0; i < index; i++) {
newNode = newNode.next;
}
newNode.prev.next = newNode.next;
newNode.next.prev = newNode.prev;
size--;
}
return newNode;
}
public int size() {
return size;
}
public static class Node {
private Object value;
private Node prev;
private Node next;
public Node(Object value, Node prev, Node next) {
this.value = value;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
return "" + value;
}
}
@Override
public Iterator iterator() {
return new Iterator() {
private int currentIndex=0;
@Override
public boolean hasNext() {
if (currentIndex>=size) {
return false;
}
return true;
}
@Override
public Object next() {
return get(currentIndex++);
}
};
}
}
一个正则表达式就是一个描述规则的字符串,所以,只需要编写正确的规则,我们就可以让正则表达式引擎去判断目标字符串是否符合规则。
正则表达式是一套标准,它可以用于任何语言。Java标准库的java.util.regex
包内置了正则表达式引擎,在Java程序中使用正则表达式非常简单。
主要包括以下三个类:
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
如果正则表达式有特殊字符,那就需要用\
转义。
要注意正则表达式在Java代码中也是一个字符串,所以,对于正则表达式\\
反斜杠来说,对应的Java字符串是"\\\\"
,因为\
也是Java字符串的转义字符,两个\\
实际上表示的是一个\
。
表达式 | 含义 |
---|---|
[abc] | a,b,c中的一个字符([]匹配中括号内的一个字符) |
[^abc] | 除a,b,c以外的任意一个字符 |
[A-a] | A-a中的一个字符(区间根据字符编码的大小,右边的编码要大于左边的编码) |
[A-z&&[^abc]] |
A-a中并且除了abc的一个字符(区间根据字符编码的大小,右边的编码要大于左边的编码) |
\\ |
反斜杠 \ |
\d | 一个0-9的数字 |
\D | 一个非0-9的字符 |
\s | 一个空白字符 |
\S | 一个非空白字符 |
\w | 一个字母、数字或下划线 |
. | 一个任意字符 |
^ | 一行的开始 |
$ | 一行的结束 |
X? | X出现0次或一次 |
X* | X出现任意次 |
X+ | X出现至少一次 |
X{n} | X出现n次 |
X{n,} | X出现至少n次 |
X{n,m} | X至少出现n次但不超过m次 |
用Pattern
对象匹配,匹配后获得一个Matcher
对象,如果匹配成功,就可以直接从Matcher.group(index)
返回子串。
public static void main(String[] args) {
Pattern p = Pattern.compile("(\\d{3,4})\\-(\\d{7,8})");
Matcher m = p.matcher("0731-12345678");
if (m.matches()) {
String g1 = m.group(1);
String g2 = m.group(2);
System.out.println(g1);
System.out.println(g2);
System.out.println(m.group());
} else {
System.out.println("匹配失败!");
}
}
/** 运行结果
* 0731
* 12345678
* 0731-12345678
**/
先看看整个字符串是否存在匹配,如果未发现匹配,则去掉字符串中的最后一个字符,再次尝试匹配,如果还是未发现匹配再去掉最后一个字符,循环往复直到发现一个匹配或者字符串不剩任何字符串。
/** 贪婪匹配 **/
public class Main {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\d+)(0*)");
Matcher matcher = pattern.matcher("1230000");
if (matcher.matches()) {
System.out.println("group1=" + matcher.group(1)); // "1230000"
System.out.println("group2=" + matcher.group(2)); // ""
}
}
}
先看看字符串的第一个字母是否存在匹配,如果未发现匹配,则读入下一个字符,再次尝试匹配,如果还是未发现匹配则再读取下一个字符,循环往复直到发现一个匹配或者整个字符串都检查过也没有发现匹配。
给定一个匹配规则,加上?
后就变成了惰性匹配。
/** 惰性匹配 **/
public class Main {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\d+?)(0*)");
Matcher matcher = pattern.matcher("1230000");
if (matcher.matches()) {
System.out.println("group1=" + matcher.group(1)); // "123"
System.out.println("group2=" + matcher.group(2)); // "0000"
}
}
}
只尝试匹配整个字符串。如果整个字符串不匹配,则不做进一步。
给定一个匹配规则,加上+后就变成了支配匹配。
/** 支配 **/
public class Main {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\d++)(0*)");
Matcher matcher = pattern.matcher("1230000");
if (matcher.matches()) {
System.out.println("group1=" + matcher.group(1)); // "1230000"
System.out.println("group2=" + matcher.group(2)); // ""
}
}
}
泛型就是编写模板代码来适应任意类型;
泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;
使用ArrayList
时,如果不定义泛型类型时,泛型类型实际上就是Object
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
// 编译器警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
String first = (String) list.get(0);
String second = (String) list.get(1);
// 无编译器警告:
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);
Pair
不是Pair
的子类;
使用 extends Number>
的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T
的上界限定在Number
了。
使用类似 extends Number>
通配符作为方法参数时表示:
Number
引用的方法,例如:Number n = obj.getFirst();
;Number
引用的方法(null
除外),例如:obj.setFirst(Number n);
。即一句话总结:使用extends
通配符表示可以读,不能写。
使用类似
定义泛型类时表示:
Number
以及Number
的子类。Pair super Integer>
表示,方法参数接受所有泛型类型为Integer
或Integer
父类的Pair
类型。
使用类似 super Integer>
通配符作为方法参数时表示:
Integer
引用的方法,例如:obj.setFirst(Integer n);
;Integer
引用的方法(Object
除外),例如:Integer n = obj.getFirst();
。即使用super
通配符表示只能写不能读。
使用extends
和super
通配符要遵循PECS原则。
无限定通配符>
很少使用,可以用
替换,同时它是所有
类型的超类。
何时使用extends
,何时使用super
?为了便于记忆,我们可以用PECS原则:Producer Extends Consumer Super。
即:如果需要返回T
,它是生产者(Producer),要使用extends
通配符;如果需要写入T
,它是消费者(Consumer),要使用super
通配符。
还是以Collections
的copy()
方法为例:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i=0; i<src.size(); i++) {
T t = src.get(i); // src是producer
dest.add(t); // dest是consumer
}
}
}
我们再回顾一下extends
通配符。作为方法参数, extends T>
类型和 super T>
类型的区别在于:
extends T>
允许调用读方法T get()
获取T
的引用,但不允许调用写方法set(T)
传入T
的引用(传入null
除外); super T>
允许调用写方法set(T)
传入T
的引用,但不允许调用读方法T get()
获取T
的引用(获取Object
除外)。一个是允许读不允许写,另一个是允许写不允许读。
File
对象既可以表示文件,也可以表示目录。特别要注意的是,构造一个File
对象,即使传入的文件或目录不存在,代码也不会出错,因为构造一个File
对象,并不会导致任何磁盘操作。只有当我们调用File
对象的某些方法的时候,才真正进行磁盘操作。
public static void main(String[] args) throws Exception {
File f=new File("/home/zengyusheng");
//Arrays.asList(f.listFiles()).forEach(System.out::println);
File[] fList=f.listFiles();
for (File file : fList) {
// if (file.getName().endsWith("h")) {
// System.out.println(file.getName());
// }
// if (file.isDirectory()) {
// System.out.println(file.getName());
// }
// if (file.isFile()) {
// System.out.println(file);
// }
// if (file.isHidden()) {
// System.out.println(file);
// }
// Path p1= Paths.get(".","study","test");
// Path p2= p1.toAbsolutePath();
// System.out.println(p2);
// Path p3=p2.normalize();
// System.out.println(p3);
// File file2=p3.toFile();
// System.out.println(file2);
System.out.println(file.getAbsolutePath());
System.out.print(file.getName()+"\t"+new Date(file.lastModified()));
if (file.isDirectory()) {
}else {
System.out.print("\t"+file.length()+" byte");
}
System.out.println();
}
}
public static void main(String[] args) {
File file=new File("/home/zengyusheng/Desktop/zys/abc/a.txt");
if (file.exists()) {
file.delete();
}else {
try {
File file2=file.getParentFile();
if (!file2.exists()) {
//file2.mkdir();
file2.mkdirs();
}
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
InputStream
就是Java标准库提供的最基本的输入流。它位于java.io
这个包里。java.io
包提供了所有同步IO的功能。
要特别注意的一点是,InputStream
并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()
,签名如下:
public abstract int read() throws IOException;
这个方法会读取输入流的下一个字节,并返回字节表示的int
值(0~255)。如果已读到末尾,返回-1
表示不能继续读取了。此方法阻塞,直到输入数据可用、检测到流的末尾或抛出异常为止。
FileInputStream in = null;
try {
//创建文件字节输入流对象
in = new FileInputStream("D:\\test\\test01\\1.txt");
//从文件读取一个字节
/* while (true) {
int i = in.read();
//当读取到文件末尾时,退出循环
if (-1 == i) {
break;
}
System.out.print((char)i);
}*/
//从文件中一次读取多个字节
byte[] bs = new byte[512];
while (true) {
int count = in.read(bs);
if (-1 == count) {
break;
}
//利用实际读取到的字节构造字符串
//String str=new String(bs); 错误
String str = new String(bs, 0, count);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
if (in != null) {
try {
//关闭文件字符输入流
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
和InputStream
相反,OutputStream
是Java标准库提供的最基本的输出流。
和InputStream
类似,OutputStream
也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)
,签名如下:
public abstract void write(int b) throws IOException;
这个方法会写入一个字节到输出流。要注意的是,虽然传入的是int
参数,但只会写入一个字节,即只写入int
最低8位表示字节的部分(相当于b & 0xff
)。
和InputStream
类似,OutputStream
也提供了close()
方法关闭输出流,以便释放系统资源。要特别注意:OutputStream
还提供了一个flush()
方法,它的目的是将缓冲区的内容真正输出到目的地。
为什么要有flush()
?因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个byte[]
数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream
有个flush()
方法,能强制把缓冲区内容输出。
通常情况下,我们不需要调用这个flush()
方法,因为缓冲区写满了OutputStream
会自动调用它,并且,在调用close()
方法关闭OutputStream
之前,也会自动调用flush()
方法。
FileOutputStream out = null;
try {
//创建文件字节输出流对象.true表示将内容写到文件末尾,不会覆盖文件原来的内容
out = new FileOutputStream("D:\\test\\test01\\2.txt", true);
//写一个字节
out.write('a');
out.write('\t');
//写多个字节
String baidu = "https://www.baidu.com";
out.write(baidu.getBytes());
//写回车换行符
out.write("\r\n".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
//关闭
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 拷贝图片
*/
public class CopyImg {
/**
* 将指定路径的图片复制到指定的文件夹中去
*
* @param imgPath
* @param destDirPath
*/
public final static void copyImg(String imgPath, String destDirPath) {
File imgFile = new File(imgPath);
if (!imgFile.isFile()) {
throw new IllegalArgumentException(imgPath + "不存在!");
}
File destDir = new File(destDirPath);
if (!destDir.isDirectory()) {
throw new IllegalArgumentException(destDirPath + "不存在!");
}
//拷贝图片
FileInputStream in = null;
FileOutputStream out = null;
BufferedInputStream bufInput = null;
BufferedOutputStream bufOutput = null;
try {
in = new FileInputStream(imgFile);//从"旧"图片文件,即源文件中读取字节
//创建带有缓冲区的字节输入流
bufInput = new BufferedInputStream(in);
//创建"新"图片文件,即备份文件
File newImg = new File(destDirPath, imgFile.getName());
if (!newImg.exists()) {
newImg.createNewFile();
}
out = new FileOutputStream(newImg);//向"新"图片文件,即备份文件中写字节
//创建带有缓冲区的字节输出流
bufOutput=new BufferedOutputStream(out);
byte[] bs = new byte[1024];
while (true) {
//从"旧"图片中读取字节
int count = bufInput.read(bs);
if (-1 == count) {
break;
}
//将读取到的字节写到"新"图片文件中
bufOutput.write(bs, 0, count);
}
} catch (Exception e) {
e.printStackTrace();
}
//关闭
if (bufInput != null) {
try {
bufInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufOutput != null) {
try {
bufOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
copyImg("C:\\Users\\luliujun\\Pictures\\class文件执行简单原理.png", "D:\\test\\test01");
}
}
public class FileCopyAllTest {
public static void main(String[] args) throws Exception {
copyFile("/home/zengyusheng/Desktop/zys", "/home/zengyusheng/Desktop/a/b");
}
public static void copyFile(String files, String path) throws Exception {
File file1 = new File(files);
String filename = null;
if (file1.isFile()) {
filename = file1.getName();
//System.out.println(filename);
InputStream in = new FileInputStream(file1);
File file2 = new File(path + "/" + filename);
if (!file2.getParentFile().exists()) {
file2.getParentFile().mkdirs();
}
file2.createNewFile();
OutputStream out = new FileOutputStream(file2);
byte[] chs = new byte[100];
while (true) {
int count = in.read(chs);
if (count == -1) {
break;
}
out.write(chs, 0, count);
}
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} else {
copyDirectory(file1, path + "/" + file1.getName());
}
}
public static void copyDirectory(File files, String path) throws Exception {
File directory = new File(path);
if(!directory.exists()) {
directory.mkdirs();
}
for (File file : files.listFiles()) {
copyFile(file.getAbsolutePath(), path);
System.out.println(file);
}
}
}
public static void main(String[] args) throws Exception {
FileOutputStream fos =new FileOutputStream("./aa.txt");
DataOutputStream doStream=new DataOutputStream(fos);
doStream.writeDouble(123.45);
doStream.writeBoolean(false);
doStream.writeInt(15);
//doStream.writeChars("hello world !");
doStream.write(5);
doStream.close();
fos.close();
}
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
FileInputStream fos =new FileInputStream("./aa.txt"); DataInputStream dis =new DataInputStream(fos); System.out.println(dis.readDouble()); System.out.println(dis.readBoolean()); System.out.println(dis.readInt());// System.out.println(dis.readByte());// System.out.println(dis.readByte());// System.out.println(dis.readByte());// System.out.println(dis.readByte());// System.out.println(dis.readChar()); System.out.println(dis.read()); dis.close(); }
序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]
数组。
为什么要把Java对象序列化呢?因为序列化后可以把byte[]
保存到文件中,或者把byte[]
通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。
有序列化,就有反序列化,即把一个二进制内容(也就是byte[]
数组)变回Java对象。有了反序列化,保存到文件中的byte[]
数组又可以“变回”Java对象,或者从网络上读取byte[]
并把它“变回”Java对象。
我们来看看如何把一个Java对象序列化。
一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable
接口,它的定义如下:
public interface Serializable {}
Serializable
接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。
public class Student implements Serializable {
private int id;
private String name;
private int age;
private boolean gender;
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 boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
}
public static void main(String[] args) throws Exception {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("./aa.txt"));
Student student =new Student();
student.setAge(18);
student.setName("dasdasd");
student.setGender(true);
oos.writeObject(student);
oos.close();
}
public static void main(String[] args) throws Exception{
ObjectInputStream obs =new ObjectInputStream(new FileInputStream("./aa.txt"));
System.out.println(obs.readObject());
obs.close();
}
字符输入流
public static void main(String[] args) {
File txt = new File("D:\\test\\test01\\2.txt");
FileReader reader = null;
try {
reader = new FileReader(txt);
char [] chs=new char[500];
while (true) {
int count = reader.read(chs);
if (-1 == count) {
break;
}
String str=new String(chs,0,count);
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class BufferedReaderDemo {
public static void main(String[] args) {
File txt = new File("D:\\test\\test01\\2.txt");
FileReader reader = null;
BufferedReader bufReader = null;
try {
reader = new FileReader(txt);
//创建缓冲字符输入流对象
bufReader = new BufferedReader(reader);
char[] chs = new char[500];
while (true) {
//读取一行
String line = bufReader.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节输出流
/**
* 字符输出流,向文本文件打印本月日历
*/
public static void main(String[] args) {
FileWriter writer = null;
try {
//writer = new FileWriter("D:\\test\\test01\\3.txt",true);
writer = new FileWriter("D:\\test\\test01\\3.txt");
Calendar c = Calendar.getInstance();
//获取本月共有多少天
int days = c.getActualMaximum(Calendar.DAY_OF_MONTH);
//当天月中的天
int date = c.get(Calendar.DAY_OF_MONTH);
//获取本月第1天周中的天
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);//1-7
writer.write("日\t一\t二\t三\t四\t五\t六");
writer.write("\r\n");
//打印本月第1天前面的空格
int b = dayOfWeek - 1;
for (int i = 1; i <= b; i++) {
writer.write(" \t");
}
//打印本月的所有日期
for (int d = 1; d <= days; d++) {
writer.write(String.valueOf(d));
if (d == date) {
writer.write('*');
}
writer.write('\t');
if (7 == dayOfWeek) {
writer.write("\r\n");
dayOfWeek = 1;
} else {
dayOfWeek++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
FileWriter writer = null;
BufferedWriter bufWriter = null;
try {
writer = new FileWriter("D:\\test\\test01\\3.txt");
/**
* 创建缓冲字符输出流对象, 参数1024表示缓冲区大小,即缓冲区可以存放1024字符
* 当缓冲区存满后,会自动刷新,即将缓冲区中所有数据写入文件中,重置缓冲区数据(将缓冲区char数组每个元素设置为'0')
* 可以调用flush() 自动刷新
*/
bufWriter = new BufferedWriter(writer, 1024);
int rows = 19;//输出菱形总行数
int bx = -rows / 2;//控制空格 变化的变量
for (int r = 1; r <= rows; r++) {
int b = Math.abs(bx);//每行输出空格数量
for (int c = 1; c <= b; c++) {
bufWriter.write(' ');
}
int s = rows - 2 * b;//每行输出*数量
for (int c = 1; c <= s; c++) {
bufWriter.write('*');
}
bx++;
bufWriter.write("\r\n");//回车换行
}
//刷新缓冲区
bufWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
if (bufWriter != null) {
try {
bufWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class SystemStream {
public static void main(String[] args) {
InputStreamReader reader = null;
BufferedReader bufReader = null;
try {
//基于"标准"输入流创建字符流
reader = new InputStreamReader(System.in);
bufReader = new BufferedReader(reader);
System.out.println("请输出一句话:");
while (true) {
String line = bufReader.readLine();
if (line == null ||0== line.length()) {
break;
}
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
if (bufReader != null) {
try {
bufReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
System.err.print("错误信息");
在计算机中,我们把一个任务称为一个进程,一个运行中的QQ就是一个进程。
线程是进程的一个执行单位。
进程和线程是包含关系,但是多任务既可以由多进程实现,也可以由单进程内的多线程实现,还可以混合多进程+多线程。
具体采用哪种方式,要考虑到进程和线程的特点。
和多线程相比,多进程的缺点在于:
而多进程的优点在于:
多进程稳定性比多线程高,因为在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会直接导致整个进程崩溃。
一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()
方法,在main()
方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。
1.通过继承Thread
类,重写run()
方法
public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run()
System.out.println("start new thread!");
}
}
2.通过实现Runnable
接口,创建线程实例时传入Runnable
实例
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});
t.start(); // 启动新线程
}
}
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
阻塞(BLOCKED):表示线程阻塞于锁。
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
终止(TERMINATED):表示该线程已终止。
线程终止的原因有:
run()
方法执行到return
语句返回;run()
方法因为未捕获的异常导致线程终止;Thread
实例调用stop()
方法强制终止(强烈不推荐使用)。线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
设定线程的优先级(1-10),默认值5
setPriority(int newPriority)
使当前执行的线程在指定的毫秒数内休眠(临时停止执行),这取决于系统计时器和调度程序的精度和准确性
sleep(long millis)
调度程序当前线程放弃对当前对处理器的使用
yield()
等待线程执行完
join() //等待线程执行完
join(long millis) //等待多少毫秒
join(long millis, int nanos) //等待多少毫秒加多少纳秒
返回对当前正在执行的线程对象的引用
currentThread()
设置线程为守护线程(true)
或用户线程`(false)
setDaemon(boolean on)
在其他线程中对目标线程调用interrupt()
方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.start();
Thread.sleep(1); // 暂停1毫秒
t.interrupt(); // 中断t线程
t.join(); // 等待t线程结束
System.out.println("end");
}
}
class MyThread extends Thread {
public void run() {
int n = 0;
while (! isInterrupted()) {
n ++;
System.out.println(n + " hello!");
}
}
}
用一个running
标志位来标识线程是否应该继续运行,在外部线程中,通过把HelloThread.running
置为false
,就可以让线程结束
public class Main {
public static void main(String[] args) throws InterruptedException {
HelloThread t = new HelloThread();
t.start();
Thread.sleep(1);
t.running = false; // 标志位置为false
}
}
class HelloThread extends Thread {
//线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。
public volatile boolean running = true;
public void run() {
int n = 0;
while (running) {
n ++;
System.out.println(n + " hello!");
}
System.out.println("end!");
}
}
为什么要对线程间共享的变量用关键字volatile
声明?
在Java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值回写到主内存,但是,这个时间是不确定的!
volatile
关键字的目的是告诉虚拟机:
守护线程是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。
如何创建守护线程呢?
调用setDaemon(true)
把该线程标记为守护线程
Thread t = new MyThread();
t.setDaemon(true);
注意:守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。
多个线程操作一个Java Heap 的对象
JVM规范定义了几种原子操作:
long
和double
除外)赋值,例如:int n = m
;List list = anotherList
。多线程同时读写共享变量时,会造成逻辑错误,因此需要通过synchronized
同步;
同步的本质就是给指定对象加锁,加锁后才能继续执行后续代码;
注意加锁对象必须是同一个实例;
对JVM定义的单个原子操作不需要同步。
用synchronized
修饰方法可以把整个方法变为同步代码块,synchronized
方法加锁对象是this
;
通过合理的设计和数据封装可以让一个类变为“线程安全”;
一个类没有特殊说明,默认不是thread-safe;
多线程能否安全访问某个非线程安全的实例,需要具体问题具体分析。
死锁产生的条件是多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待;
public class DeadLockTest {
static StringBuffer sb1=new StringBuffer();
static StringBuffer sb2=new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized(sb1) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
}
}
System.out.println(sb1);
System.out.println(sb2);
};
}.start();
new Thread() {
public void run() {
synchronized(sb2) {
sb1.append("C");
synchronized (sb1) {
sb2.append("D");
}
}
System.out.println(sb1);
System.out.println(sb2);
};
}.start();
}
}
wait
和notify
用于多线程协调运行:
synchronized
内部可以调用wait()
使线程进入等待状态;wait()
方法;synchronized
内部可以调用notify()
或notifyAll()
唤醒其他等待线程;notify()
或notifyAll()
方法;JDK提供了ExecutorService
实现了线程池功能:
Executors
提供了静态方法创建不同类型的ExecutorService
;shutdown()
关闭ExecutorService
;ScheduledThreadPool
可以定期调度多个任务。因为ExecutorService
只是接口,Java标准库提供的几个常用实现类有:
public class ThreadPoolFinalTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(20);
List<Future<Result>> results = new ArrayList<>();
Map<Future<Result>,Task> futureTask=new HashMap<>();
for (int i = 0; i < 20; i++) {
Task task=new Task(i + 1);
Future<Result> future = pool.submit(task);
results.add(future);
futureTask.put(future,task);
}
while(true){
for (int i = 0; i < results.size(); i++) {
Future<Result> future= future =results.get(i);
if (future.isDone()){
try {
Result result = future.get();
System.out.println(result);
} catch (Throwable e) {
System.err.println(e.getMessage());
Task task=futureTask.get(future);
Future<Result> future1=pool.submit(task);
results.add(future1);
futureTask.put(future1,task);
}
results.remove(i);
i--;
}
}
if (results.isEmpty()){
break;
}
}
pool.shutdown();
}
public static class Result {
private int id;
private String str;
public Result(int id, String str) {
this.id = id;
this.str = str;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Result [id=" + id + ", str=" + str + "]";
}
}
private static class Task implements Callable<Result> {
private Random random = new Random();
private int id = -1;
StringBuffer buffer = new StringBuffer();
public Task(int id) {
this.id = id;
}
@Override
public Result call() throws Exception {
for (int i = buffer.length(); i < 5; i++) {
int type = random.nextInt(3);
char ch = '\0';
int bound = 0;
switch (type) {
case 0:
bound = random.nextInt(9);
break;
case 1:
case 2:
bound = random.nextInt(26);
break;
}
if (bound==0){
throw new RuntimeException("buffer:"+buffer+",bound:"+bound+",id:"+id);
}
switch (type) {
case 0:
ch = (char) (bound + '0');
break;
case 1:
ch = (char) (bound + 'a');
break;
case 2:
ch = (char) (bound + 'A');
break;
}
buffer.append(ch);
}
try {
int sleepTime = random.nextInt(15);
System.out.println("sleeptime:" + sleepTime + ",id:" + id);
Thread.sleep(sleepTime * 1000);
} catch (Exception e) {
}
return new Result(this.id, buffer.toString());
}
}
}
XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。
XML有固定的结构,首行必定是,可以加上可选的编码。紧接着,如果以类似
声明的是文档定义类型(DTD:Document Type Definition),DTD是可选的。接下来是XML的文档内容,一个XML文档有且仅有一个根元素,根元素可以包含任意个子元素,元素可以包含属性,例如,
包含一个属性lang="CN"
,且元素必须正确嵌套。如果是空元素,可以用
表示
Java提供的DOM API可以将XML解析为DOM结构,以Document对象表示;
DOM可在内存中完整表示XML数据结构;
DOM解析速度慢,内存占用大。
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder=factory.newDocumentBuilder();
File xml=new File("src/cn/zys1314/javase/xml/students.xml");
Document document = documentBuilder.parse(xml);
Element element = document.getDocumentElement();
NodeList childNodes = element.getChildNodes();
for (int i = 0; i <childNodes.getLength(); i++) {
Node item = childNodes.item(i);
//System.out.println(item);
if (item instanceof Element){
Element ele=(Element) item;
System.out.println(ele.getNodeName());
NodeList name = ele.getElementsByTagName("name");
System.out.println(name.item(0).getTextContent());
}
}
}
有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。Java标准库已经定义了一些元注解,我们只需要使用元注解,通常不需要自己去编写元注解。
最常用的元注解是@Target
。使用@Target
可以定义Annotation
能够被应用于源码的哪些位置:
ElementType.TYPE
;ElementType.FIELD
;ElementType.METHOD
;ElementType.CONSTRUCTOR
;ElementType.PARAMET
另一个重要的元注解@Retention
定义了Annotation
的生命周期:
RetentionPolicy.SOURCE
;RetentionPolicy.CLASS
;RetentionPolicy.RUNTIME
。使用@Repeatable
这个元注解可以定义Annotation
是否可重复。这个注解应用不是特别广泛。
使用@Inherited
定义子类是否可继承父类定义的Annotation
。@Inherited
仅针对@Target(ElementType.TYPE)
类型的annotation
有效,并且仅针对class
的继承,对interface
的继承无效
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
可以在运行期通过反射读取RUNTIME
类型的注解
可以通过程序处理注解来实现相应的功能
@Component
public class Component2 {
@Auth
public void test(){}
}
// 包扫描
public class PackageScan {
public static void main(String[] args) {
Class clazz = PackageScan.class;
Package pkg = clazz.getPackage();
String rootPkg = pkg.getName().split("\\.")[0];
URL url = clazz.getResource("/" + rootPkg);
String filePath = null;
if (url.getProtocol().startsWith("file")) {
filePath = url.getFile();
File file = new File(filePath);
printComponent(file, rootPkg);
}
}
public static void printComponent(File file, String rootPkg) {
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
printComponent(f, rootPkg + "." + f.getName());
}
if (f.isFile()) {
if(f.getName().endsWith(".class")){
String className = f.getName().substring(0, f.getName().lastIndexOf("."));
className=rootPkg + "." + className;
try {
Class cls=Class.forName(className);
if (cls.isAnnotationPresent(Component.class)) {
System.out.println(cls.getName());
}
Method[] methods = cls.getMethods();
for (Method method:methods) {
if (method.isAnnotationPresent(Auth.class))
System.out.println(method);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
}
JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。
使用JDBC的好处是:
1.加载JDBC驱动程序
2.建立数据库连接
3.创建Statement对象
4.执行SQL语句
5.处理返回结果
6.释放资源
public class TestJDBC {
public static void main(String[] args) throws Exception {
/**
* 1.加载驱动类 Driver 2.获取连接 3.获取操作句柄 4.操作数据库(发送SQL语句) 5.释放资源
*/
/*
* try { Class.forName("oracle.jdbc.driver.OracleDriver"); //SPI } catch
* (ClassNotFoundException e) { e.printStackTrace(); }
*/
String url = "jdbc:oracle:thin:@localhost:1521:helowin";
String username = "zengyusheng";
String password = "qwe123";
Connection conn = null;
try {
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
Statement statement = conn.createStatement();
statement.executeUpdate("update student set name='罗炫' where id=5");
String sql = "select id,name,subject,score from student";
ResultSet row = statement.executeQuery(sql);
while (row.next()) {
System.out.print(row.getInt(1) + "\t");
System.out.print(row.getString(2) + "\t");
System.out.print(row.getString(3) + "\t");
System.out.print(row.getString(4) + "\n");
}
row.close();
statement.close();
conn.close();
}
}
DriverManager类是JDBC的管理层,作用于应用程序和驱动程序之间,并在数据库和相应驱动程序之间建立连接.该类负责加载、注册JDBC驱动程序.使用DriverManager类的getConnection()获得一个数据库连接
Connection conn = DriverManager.getConnection(String url,String user,String password);
数据库连接接口
用于执行SQL语句,并获取查询结果,Statement接口提供了三种执行SQL语句的方
预编译SQL语句Statement接口,SQL语句被预编译后并存储在 PreparedStatement 子类对象中,然后可以使用此对象多次高效地执行该语句
注:用于设置 IN 参数值的设置方法(setShort、setString 等等)必须指定与输入参数的已定义 SQL 类型兼容的类型.例如,如果 IN 参数具有 SQL 类型 INTEGER,那么应该使用 setInt 方法
public static void main(String[] args) throws Exception {
FileInputStream fis =new FileInputStream("/home/zengyusheng/Pictures/OSI七层模型.png");
Connection conn =JDBCUtils.getConnection();
String sql ="insert into testblob(id,test) values(?,?)";
PreparedStatement ps =null;
try {
ps =conn.prepareStatement(sql);
ps.setInt(1, 1);
ps.setBinaryStream(2, fis);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
fis.close();
ps.close();
conn.close();
}
}
ResultSet表示查询结果,通常通过执行查询数据库的语句生成
JDBC驱动程序将Java数据类型转换为适当的JDBC类型,然后将其发送到数据库. 它为大多数数据类型提供并使用默认映射.
SQL类型 | JDBC类型 | Java类型 |
---|---|---|
VARCHAR | java.sql.Types.VARCHAR | java.lang.String |
CHAR | java.sql.Types.CHAR | java.lang.String |
LONGVARCHAR | java.sql.Types.LONGVARCHAR | java.lang.String |
BIT | java.sql.Types.BIT | boolean |
NUMERIC | java.sql.Types.NUMERIC | java.math.BigDecimal |
TINYINT | java.sql.Types.TINYINT | byte |
SMALLINT | java.sql.Types.SMALLINT | short |
INTEGER | java.sql.Types.INTEGER | int |
BIGINT | java.sql.Types.BIGINT | long |
REAL | java.sql.Types.REAL | float |
FLOAT | java.sql.Types.FLOAT | float |
DOUBLE | java.sql.Types.DOUBLE | double |
VARBINARY | java.sql.Types.VARBINARY | byte[ ] |
BINARY | java.sql.Types.BINARY | byte[ ] |
DATE | java.sql.Types.DATE | java.sql.Date |
TIME | java.sql.Types.TIME | java.sql.Time |
TIMESTAMP | java.sql.Types.TIMESTAMP | java.sql.Timestamp |
CLOB | java.sql.Types.CLOB | java.sql.Clob |
BLOB | java.sql.Types.BLOB | java.sql.Blob |
ARRAY | java.sql.Types.ARRAY | java.sql.Array |
REF | java.sql.Types.REF | java.sql.Ref |
STRUCT | java.sql.Types.STRUCT | java.sql.Struct |
jdbc.oracle.drivername=oracle.jdbc.driver.OracleDriver
jdbc.oracle.url=jdbc:oracle:thin:@localhost:1521:helowin
jdbc.oracle.username=zengyusheng
jdbc.oracle.password=qwe123
public class JDBCUtils {
private static Properties pro = null;
private static final String JDBC_ORACLE_DRIVERNAME = "jdbc.oracle.drivername";
private static final String JDBC_ORACLE_USERNAME = "jdbc.oracle.username";
private static final String JDBC_ORACLE_PASSWORD = "jdbc.oracle.password";
private static final String JDBC_ORACLE_URL = "jdbc.oracle.url";
static {
pro = new Properties();
InputStream is = JDBCUtils.class.getResourceAsStream("/db.properties");
try {
if (null != is) {
pro.load(is);
}
Class.forName(pro.getProperty(JDBC_ORACLE_DRIVERNAME));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new JDBCUtils();
}
public static Connection getConnection() {
Connection coon = null;
for (int i = 0; i < 3; i++) {
try {
coon = DriverManager.getConnection(pro.getProperty(JDBC_ORACLE_URL),
pro.getProperty(JDBC_ORACLE_USERNAME),
pro.getProperty(JDBC_ORACLE_PASSWORD));
return coon;
} catch (SQLException e) {
}
}
return coon;
}
public static void close(Connection connection,PreparedStatement ps) {
try {
if (ps!=null) {
ps.close();
}
if (connection!=null) {
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
com.qst.jdbc.mysql.driver=com.mysql.jdbc.Driver
com.qst.jdbc.mysql.user=root
com.qst.jdbc.mysql.password=123456
com.qst.jdbc.mysql.url=jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf8
Connection conn = null;
try {
ResourceBundle rb = ResourceBundle.getBundle("com.qst.javaee.config.db");
String driver = rb.getString("com.qst.jdbc.mysql.driver");
String user = rb.getString("com.qst.jdbc.mysql.user");
String password = rb.getString("com.qst.jdbc.mysql.password");
String url = rb.getString("com.qst.jdbc.mysql.url");
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
throw new RuntimeException(e);
}
JDBC连接池有一个标准的接口javax.sql.DataSource
,注意这个类位于Java标准库中,但仅仅是接口。要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。常用的JDBC连接池有:
目前使用最广泛的是HikariCP
public class TestDataSource {
public static void main(String[] args) throws Exception {
HikariDataSource dataSource=new HikariDataSource(); dataSource.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:helowin"); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername("zengyusheng");
dataSource.setPassword("qwe123");
long start=System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
Connection conn =dataSource.getConnection();
// Connection conn=JDBCUtils.getConnection();
conn.close();
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
public class PageSplit<T> {
private int currentPage;
private int pageSize;
private int totalCount;
private int pageCount;
private List<T> list=new ArrayList<>();
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
public static PageSplit<Student> get(int currentPage, int pageSize) {
PageSplit<Student> pages = new PageSplit<Student>();
Connection conn = JDBCUtils.getConnection();
if (null == conn) {
return pages;
}
int totalCount=0;
try {
ResultSet resultSet=conn.createStatement().executeQuery("select count(1) from student2");
if (resultSet.next()) {
totalCount=resultSet.getInt(1);
}
} catch (SQLException e1) {
e1.printStackTrace();
}
pages.setTotalCount(totalCount);
int pageCount=(totalCount+pageSize-1)/pageSize;
pages.setPageCount(pageCount);
if (currentPage<=0) {
currentPage=1;
}
if (currentPage>=pageCount) {
currentPage=pageCount;
}
pages.setCurrentPage(currentPage);
pages.setPageSize(pageSize);
ResultSet resultSet =null;
PreparedStatement ps =null;
String pageSplitSql="select id,name,age,gender from (select rownum r,id,name,age,gender from student2 where rownum<=?"
+ ") t where t.r>?";
try {
ps =conn.prepareStatement(pageSplitSql);
ps.setInt(1, currentPage*pageSize);
ps.setInt(2, (currentPage-1)*pageSize);
resultSet=ps.executeQuery();
System.out.println(pageSplitSql);
List<Student> students=new ArrayList<Student>();
while(resultSet.next()) {
Student student=new Student();
student.setId(resultSet.getInt(1));
student.setName(resultSet.getString(2));
student.setAge(resultSet.getInt(3));
student.setGender(resultSet.getBoolean(4));
students.add(student);
}
pages.setList(students);
return pages;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(conn, ps);
}
return null;
}