链接:java开发手册
提取码:kes8
链接:JDK1.8详细文档
提取码:n9zo
JavaSE 面向对象编程(Object-oriented programming)
封装
类(class)
类的声明
两种类型的类声明:
普通类声明
class 类名 {}
枚举声明
enum 类名 {}
PS:如果一个类与其任何包含的类或接口具有相同的简单名称,则是编译时错误。
类的构成
成员变量(属性),成员方法,构造方法,初始化块,静态块
对象
对象是类的具体实现。一切皆对象。
class和Java源文件的关系
- 类的 名称和Java源文件的名称必须保持一致
- Java源文件只能包含一个public class
- Java源文件如果包含多个类,则会生成多个.class文件
Package
作用:防止名称冲突,好区分同名类。
package语句必须放在类定义的最前面
访问修饰符
public 公开的、共享的、 private 私有的 、 protected 受保护的 、缺省或默认的访问权限
private访问权限:同类
public访问权限:所有人
protected访问权限:同类,同包,子类
默认的访问权限:同类,同包
构造方法
-
方法名称跟类名保持一致;
-
没有定义构造方法时,编译器会自动创建一个默认的无参的构造方法;
-
构造方法的作用是在new的时候,初始化对象的属性,或者做其他初始化工作;
JVM规范定义:所有的构造方法在编译之后的方法名为<init>,返回的类型为Class类型,只能被JVM调用。
方法重载
重载的条件:
- 同一个类中
- 方法名相同
- 两个及以上个数的方法的参数类型不同或参数的个数不同或参数的顺序不同
- 不看返回类型
this
指向当前对象
this()可以调用本类的重载构造方法,且this()调用必须放在第一行
static
静态属性:能被所有对象所共享,在内存中只有一个副本,它当且仅当在类的初始化加载时会被初始化。
静态方法:在静态方法中不能访问非静态成员变量和非静态成员方法。
静态代码块:static块可以置于类中的任何地方,类中可以有多个static块。在初始化被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
通过类名.静态方法或静态属性调用。
对象的构建顺序
父类的静态块->子类的静态块->父类的初始化块->父类的构造方法子类的->子类的初始化块->子类的构造方法
静态块:在类的加载期间,做static初始化工作,针对于当前类的静态属性。
初始化块:初始化块在虚拟机编译之后,会生成一个<clinit>方法
继承
所有类的父类都指向Object类
super
指向当前父类的对象
super()来调用父类的构造方法,且super()调用必须放在第一行
Abstract
Abstract Class(抽象类)
抽象类,不具体、不完整的类。
当一个类定义为抽象类之后,就不能被实例化。
抽象类可以没有抽象方法,有抽象方法的类一定是抽象类。
当一个非抽象子类继承了抽象类就必须对抽象方法进行实现,并且子类的方法和参数必须和父类保持一致。
抽象方法
abstract可以用来修饰方法,代表这个方法为抽象方法,此方法不能有具体的实现。
方法重写
- 子类重写父类的方法
- 子类的访问修饰符要大于或等于父类的访问修饰符
- 子类的返回类型要小于或等于父类的返回类型
- 子类的方法参数必须跟父类保持一致
- 父类如果throws是CheckedException,那么子类声明的时候,CheckedException一定是小于父类的
- 父类如果throws是RuntimeException,那么子类声明的时候,可以忽略
多态
- 父类的引用指向子类的对象
父类的引用只能调用父类定义的方法
父类的引用如果要调用子类定义的方法,需要将父类的引用强转为子类的类型
强制转换的时候,建议使用instanceof运算符进行判断(防止对象的引用是引用数据类型的特殊值null)
- 多态是指,针对某个类型方法的调用,其真正执行的方法取决于运行时期实际类型的方法
final
修饰类
当前类不能被继承
修饰方法
当前方法不能被Override
修饰变量
- 栈区的值不能被更改;
- final修饰local variable 这个值只能被赋值一次;
- final修饰属性,此属性必须在构造方法里,或构造方法之前赋值,且只能被赋值一次;
- final修饰引用数据类型,可以修改堆中的数据。
接口(Interface )
- 接口不能实例化
- 所有成员变量被public static final修饰
- 一个类可以实现(implements)多个接口
接口和抽象类的区别
接口是对行为的规范,抽象类是对数据模板的规范。
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
数组
数组是一个包含固定数量的单一类型值的容器对象。 创建数组时将确定数组的长度
数组的创建方式
int[] anArray = new int[10];
int[] anArray = { 100, 200, 300};
访问数组下标为i的元素
数组的首地址+i*数组的元素的大小
特点
- 大小一致
- 长度固定
- 排列连续
内部类
指的的一个public类的内部定义另外一个类
分类
- 静态内部类(Static Nested Class)
- 非静态内部类(Inner Class)
- 匿名内部类(Anonymous Class)
Java.lang学习笔记
java.lang.Object
-
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();
}
}
java.lang.String
- String对象一旦创建后,其栈区内容、长度不可以改变。(由final修饰的char数组)
- String对象可以存放在常量区,堆内存中。
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);
java.lang.StringBuilder
线程非安全的,多线程环境效率高于StringBuffer
java.lang.StringBuffer
线程安全的,同步的.现实方式和使用与StringBuilder相同
手写简化版的StringBuffer
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;
}
}
java.lang.Math
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... 每次都不一样
包装类型
- int—————————->Integer
- short————————->Short
- long—————————>Long
- byte—————————>Byte
- float————————–>Float
- double————————>Double
- char—————————–>Character
- boolean————————>Boolean
自动装箱和自动拆箱
- 装箱:基本数据类型->包装类对象
new Integer(2);
- 拆箱:包装类对象->基本数据类型
Integer k = new Integer(1); int y = k.intValue();
- 自动装箱
Integer j=1;
- 自动拆箱
int i=new Integer(2);
java.lang.System
// 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];}
异常和错误关系
- 异常属于应用程序级别的,程序员需要进行处理
- 错误属于系统级别的,是应用程序不应该试图捕获的严重问题,程序员无需处理
- 异常父类(java.lang.Exception)和错误父类(java.lang.Error)都继承于java.lang.Throwable
CheckedException
处理(必须要处理)
- 使用try… catch
- 在方法声明上throws Exception1,…(throws是指,如果当前方法里面有定义的throws Exception,就直接抛给调用者。如果调用了包括checkedException的throws语句的方法,这个时候,调用的地方也要处理。)
RuntimeException
常见的RuntimeException
算术异常: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-catch-finally)
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);
}
}
Java.util学习笔记
java.util.Date
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);
java.util.Calendar
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);
}
}
java.util.Random
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.util.Collection
Java标准库自带的java.util
包提供了集合类:Collection
,它是除Map
外所有其他集合类的根接口。
集合类
ArrayList
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
Vector 实现方式与ArrayList相同,为大小可变的”动态数组”.
Vector与ArrayList区别:
1.Vector是线程同步的,即线程安全的;而Arraylist是线程非安全的.如果不考虑到线程的安全因素,一般用Arraylist效率比较高。
2.如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而Arraylist增长率为目前数组长度的50%.如果在集合中使用数据量比较大的数据,用Vector有一定的优势。
Stack
Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法
LinkedList
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);
}
HashMap
基于哈希表的 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);
}
Hashtable
此类实现一个哈希表 ,该哈希表将键映射到相应的值 ,任何非 null 对象都可以用作键或值 .
Hashtable与HashMap的区别
HashMap | HashTable | |
---|---|---|
线程安全 | 非线程安全 | 线程安全 |
底层数据结构 | Object数组+链表+红黑树 | Object数组+链表 |
Null支持 | Key和Value都可以取Null值 | Key不可以取Null值 |
初始容量和扩容 | 默认容量是16,扩容为2N | 默认初始容量是11,扩容为2N+1 |
效率 | 效率高 | 效率低、已经弃用 |
TreeMap
基于红黑树 (Red -Black tree)NavigableMap 实现 .该映射根据其键的自然顺序进行排序或者根据创建映射时提供的 Comparator 进行排序 .
要点:
- 实现不是同步的
- 不允许null的key,但允许null的value
Set
Set集合的特点就是无序,不可重复,对比List集合是有序的,可以重复。
-
HashSet:HashSet的底层其实就是一个HashMap,
只是我们add的时候只使用了HashMap的key部分。 -
TreeSet:可以对Set集合进行排序,默认自然排序。
Set接口常见的实现类有哪些? -
TreeSet存放自定义类型如何实现有序的?
分别是采用Comparable接口和Comparator来实现
java.util.Iterator
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);
}
}
}
手写简化版LinkedList
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程序中使用正则表达式非常简单。
java.util.regex
主要包括以下三个类:
-
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);
extends
Pair<Integer>
不是Pair<Number>
的子类;
使用<? extends Number>
的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T
的上界限定在Number
了。
使用类似<? extends Number>
通配符作为方法参数时表示:
- 方法内部可以调用获取
Number
引用的方法,例如:Number n = obj.getFirst();
; - 方法内部无法调用传入
Number
引用的方法(null
除外),例如:obj.setFirst(Number n);
。
即一句话总结:使用extends
通配符表示可以读,不能写。
使用类似<T extends Number>
定义泛型类时表示:
- 泛型类型限定为
Number
以及Number
的子类。
super
Pair<? super Integer>
表示,方法参数接受所有泛型类型为Integer
或Integer
父类的Pair
类型。
使用类似<? super Integer>
通配符作为方法参数时表示:
- 方法内部可以调用传入
Integer
引用的方法,例如:obj.setFirst(Integer n);
; - 方法内部无法调用获取
Integer
引用的方法(Object
除外),例如:Integer n = obj.getFirst();
。
即使用super
通配符表示只能写不能读。
使用extends
和super
通配符要遵循PECS原则。
无限定通配符<?>
很少使用,可以用<T>
替换,同时它是所有<T>
类型的超类。
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和super通配符
我们再回顾一下extends
通配符。作为方法参数,<? extends T>
类型和<? super T>
类型的区别在于:
<? extends T>
允许调用读方法T get()
获取T
的引用,但不允许调用写方法set(T)
传入T
的引用(传入null
除外);<? super T>
允许调用写方法set(T)
传入T
的引用,但不允许调用读方法T get()
获取T
的引用(获取Object
除外)。
一个是允许读不允许写,另一个是允许写不允许读。
Java.io学习笔记
File
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
InputStream
就是Java标准库提供的最基本的输入流。它位于java.io
这个包里。java.io
包提供了所有同步IO的功能。
要特别注意的一点是,InputStream
并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()
,签名如下:
public abstract int read() throws IOException;
这个方法会读取输入流的下一个字节,并返回字节表示的int
值(0~255)。如果已读到末尾,返回-1
表示不能继续读取了。此方法阻塞,直到输入数据可用、检测到流的末尾或抛出异常为止。
FileInputStream
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();
}
}
OutputStream
FileOutputStream
和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);
}
}
}
DataOutputStream
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();
}
DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 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),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。
ObjectOutputStream
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();
}
ObjectInputStream
public static void main(String[] args) throws Exception{
ObjectInputStream obs =new ObjectInputStream(new FileInputStream("./aa.txt"));
System.out.println(obs.readObject());
obs.close();
}
Reader
字符输入流
FileReader
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();
}
}
}
BufferReader
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();
}
}
}
}
Writer
字节输出流
FileWriter
/**
* 字符输出流,向文本文件打印本月日历
*/
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();
}
}
}
BufferedWriter
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();
}
}
}
标准流
- System.out :“标准”输出流
- System.in:“标准”输入流
- System.err:“标准”错误输出流
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就是一个进程。
线程
线程是进程的一个执行单位。
进程和线程
进程和线程是包含关系,但是多任务既可以由多进程实现,也可以由单进程内的多线程实现,还可以混合多进程+多线程。
具体采用哪种方式,要考虑到进程和线程的特点。
和多线程相比,多进程的缺点在于:
- 创建进程比创建线程开销大,尤其是在Windows系统上;
- 进程间通信比线程间通信要慢,因为线程间通信就是读写同一个变量,速度很快。
而多进程的优点在于:
多进程稳定性比多线程高,因为在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会直接导致整个进程崩溃。
Java多线程
一个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个阶段,包括:新建、就绪、运行、阻塞、销毁。
- 新建:就是刚使用new方法,new出来的线程;
- 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
- 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
- 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
线程类的方法
设定线程的优先级(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<String> 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
wait
和notify
用于多线程协调运行:
- 在
synchronized
内部可以调用wait()
使线程进入等待状态; - 必须在已获得的锁对象上调用
wait()
方法; - 在
synchronized
内部可以调用notify()
或notifyAll()
唤醒其他等待线程; - 必须在已获得的锁对象上调用
notify()
或notifyAll()
方法; - 已唤醒的线程还需要重新获得锁后才能继续执行。
线程池
JDK提供了ExecutorService
实现了线程池功能:
- 线程池内部维护一组线程,可以高效执行大量小任务;
Executors
提供了静态方法创建不同类型的ExecutorService
;- 必须调用
shutdown()
关闭ExecutorService
; ScheduledThreadPool
可以定期调度多个任务。
因为ExecutorService
只是接口,Java标准库提供的几个常用实现类有:
- FixedThreadPool:线程数固定的线程池;
- CachedThreadPool:线程数根据任务动态调整的线程池;
- SingleThreadExecutor:仅单线程执行的线程池。
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学习笔记
XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。
XML的结构
XML有固定的结构,首行必定是<?xml version="1.0"?>
,可以加上可选的编码。紧接着,如果以类似<!DOCTYPE note SYSTEM "book.dtd">
声明的是文档定义类型(DTD:Document Type Definition),DTD是可选的。接下来是XML的文档内容,一个XML文档有且仅有一个根元素,根元素可以包含任意个子元素,元素可以包含属性,例如,<isbn lang="CN">1234567</isbn>
包含一个属性lang="CN"
,且元素必须正确嵌套。如果是空元素,可以用<tag/>
表示
Dom
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
。使用@Target
可以定义Annotation
能够被应用于源码的哪些位置:
- 类或接口:
ElementType.TYPE
; - 字段:
ElementType.FIELD
; - 方法:
ElementType.METHOD
; - 构造方法:
ElementType.CONSTRUCTOR
; - 方法参数:
ElementType.PARAMET
@Retention
另一个重要的元注解@Retention
定义了Annotation
的生命周期:
- 仅编译期:
RetentionPolicy.SOURCE
; - 仅class文件:
RetentionPolicy.CLASS
; - 运行期:
RetentionPolicy.RUNTIME
。
@Repeatable
使用@Repeatable
这个元注解可以定义Annotation
是否可重复。这个注解应用不是特别广泛。
@Inherited
使用@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学习笔记
JDBC简介
JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。
使用JDBC的好处是:
- 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发;
- Java程序编译期仅依赖java.sql包,不依赖具体数据库的jar包;
- 可随时替换底层数据库,访问数据库的Java代码基本不变。
JDBC的使用
使用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
DriverManager类是JDBC的管理层,作用于应用程序和驱动程序之间,并在数据库和相应驱动程序之间建立连接.该类负责加载、注册JDBC驱动程序.使用DriverManager类的getConnection()获得一个数据库连接
Connection conn = DriverManager.getConnection(String url,String user,String password);
Connection
数据库连接接口
Statement
用于执行SQL语句,并获取查询结果,Statement接口提供了三种执行SQL语句的方
- executeQuery():用于产生单个结果集的语句,例如select语句.
- executeUpdate():用于执行insert、update或者delete语句以及DDL语句.
- execute():执行给定的SQL语句,可以是任何SQL语句
PreparedStatement
预编译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
ResultSet表示查询结果,通常通过执行查询数据库的语句生成
JDBC数据类型
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 |
使用Properties 读取配置文件
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连接池
JDBC连接池有一个标准的接口javax.sql.DataSource
,注意这个类位于Java标准库中,但仅仅是接口。要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。常用的JDBC连接池有:
- HikariCP
- C3P0
- BoneCP
- Druid
目前使用最广泛的是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;
}