-:2个保留字(goto,const);3个直接量(true,false,null)
-:标示符:字母、中文、下划线_或者$开头,符号只可能有$和_
-:单行注释、块注释、文档注释(提取信息如作者@author,参数@param;javadoc xxx.java命令生成此类的文档)
-:包装类,使其具有面向对象特征,提供属性和方法,丰富操作,默认值null
基本数据类型与包装类 | 表示范围 | 字节(1字节=8位) | ||
byte-Byte字节 | -128-127 | 1 | 1byte=8bit; 1KB=1024byte; 1MB=1024KB; 1GB=1024MB; |
|
short-Short短整 | -2`15(-32768)-2`15-1(32767) | 2 | ||
int-Integer整 | -2^31-2`31-1 | 4 | 整数默认类型 | 2/8/16进制的转换:Integer.toBinaryString/toOctalString/toHexString ((value); 转为任意进制:Integer.toString (value,n); |
long-Long长整 | 8 | 默认值0L,在int型范围内的可不加l/L(自动类型转换) | long l = 10L; long l =10; |
|
float-Float单精浮点 | 4 | 赋值须带f/F,即默认值0.0f/F;但当右边数字没有小数点,则JVM认为是int型,此时不加也可以(自动类型转换) | ||
double-Double双精浮点 | 8 | 浮点数默认类型即可省略d/D; 浮点数采用科学计数法,丢失数据即1*0.1!=0.1;比较则转为>=和<=: 特殊的浮点数(用来表示溢出和错误): 1、正无穷大POSITIVE_INFINITY(正浮点数除以0;且都相等) 2、负无穷大NEGATIVE_INFINITY(负浮点数除以0;且都相等) 3、非数NaN(0.0除以0.0或者负数开方;且不与任何数和其他NaN相等);整数除以0则抛出异常 |
BigDecimal:用于金融方面,保持精度; BigInteger:用于大整数 加:add(anotherBd); 减:subtract(anotherBd); 乘:multiply(anotherBd); 除:divide(anotherBd); |
|
boolean-Boolean布尔 | 1 | 默认false; 参与字符串运算时被提升为字符串; |
||
char-Character字符 | 编码相关;Unicode编码(16位)占2字节 | 默认‘\0’; 可赋值为汉字;不能双引号; 特殊字符如换行’\n’,制表’\t’; 可直接使用Unicode值表示如’\uXXX’;还可表示0-65535内的整数 |
-:自动类型转换:范围低->范围高;特殊的char不与byte和short转换;byte可以转为short
-:强制类型转换:int i=233;byte b=(byte)i;b将输出-23
过程:byte为8位,转换时丢弃int32位的前24位;最高位为符号位(233的二进制后右边8位的最高位是1),表示负(而负数以补码形式存储,所以后七位应-1转为反码再转为原码);
-:表达式自动类型提升:注意返回类型是否兼容
对byte,char,short参与的表达式运算,自动提升为int型,结果为int型;即使byte + short也 = int;
byte a=1+2;//正确
byte a=1;byte b=2;//计算a+b则返回int
short a=1;a+=1;//正确;+=如同=,赋值运算(一次运算)
short a=1;a=a+1;//错误;算数运算(二次运算)
-:八进制以0开头;十六进制以0x或0X开头(10-15用不区分大小写的a-f表示);二进制(JDK1.7后以0b或0B开头)最高位表符号
Integer i
= new Integer(1);//构造器
=Integer.valueOf(1);//静态方法
=new Integer(“1”);//String类型参数的构造器
=valueOf(“1”);//String参数类型的静态方法
=1;//JDK1.5自动装箱;可与基本类比较大小;Object b=true;
NumberFormatException;参数为忽略大小写的”true”或“false”时,创建true或false对应的Boolean对象;
包装类之间的比较:值在-128-127,则true(java设计自动包装-128-127缓存都一个数组中,像常量池,所以每次比较都是同一个地方取出),即如果new,则==为false,如果直接Integer i=(-128-127),则==返回true;不在范围内的,都会先转为创建实例new,再比较(即引用类型的比较);静态方法compare(value1,value2):参数为基本数据类型;true>false;返回值1或0或-1
int i
=包装类对象.intValue();//其他基本类型同理
=包装类对象;//自动拆箱
包装类对象+=1;//完成的动作有:先拆箱,完成int的+运算以后,再被装箱
转换->String:变量/对象.toString();String静态方法valueOf(基本类型变量);+连接
转为->基本数据类型:包装类.parseXXX(string);构造器
ASCII字符集:7位存储;但用1字节(8位)存储,提高读写效率
数字0 - 9:char>=48 && char<=57,或者char==’0/1/2…’
A - Z:char>=65 && char<=90,或者char==’A/B/C…’
a - z:char>=97 && char<=122,或者char==’a/b/c…’
ISO-8859-1:单字节编码,0-255,仅用于英文
GB2312(简体) -> GBK(兼容并扩展到繁体):双字节编码,汉字国标码;英文兼容上者
Unicode:一般定长双字节编码,所有语言百万字符,最统一;不便传输和存储;16进制方式;java使用
UTF:不定长编码,一般英文一字节,汉字三字节;UTF-8为1至6个字节变长存储;UTF-32为4字节定长存储;UFT-16介于两者之间,2或4字节可变可定
算术运算符:/用于浮点数时,结果为浮点数且除数可以为0或0.0;%运算用于浮点数(舍弃结果的整数后的余数)除以0/0.0时得到NaN,用于被除数则得到0或0.0;-可用作求负运算
比较运算符:true==false返回false
逻辑运算符: &&,||(&和|不短路;短路则右边直接舍弃),^异或(同则false,不同则true),!
位运算符:针对整数(byte,char,short被提升为int);所移位数超过范围时先取余(如int型移动33位等于移动1位)
&(11得1); |(00得0);~(按位取反);^(同得0,不同得1);<<(左边高位被移除,右边以0填充;乘2的移动次幂);>>(左边以原符号位填充;除以2的移动次幂);>>>(无符号右移)
byte,char,short,int,新增枚举,String(非缓冲字符串)
条件与任意case都不匹配则执行default;可在switch中的任意位置;若default位于第一行,则不管expression与case中的value是否匹配,程序会从default开始执行直到第一个break出现
for循环的初始化变量可以多个,但一定同类型
break结束循环;当需要直接结束外层循环时,循环外声明一个特殊标识符(如end:)标识某层循环,然后break end;结束该层循环
continue结束本次循环(即忽略其后的代码),开始下一次循环;可配合标识符
foreach循环:自动遍历数组或集合的元素;for(type name:Array/Collection);无须长度和索引且自动迭代(一次后结束循环);不适用修改元素值
存储同类型(包括子类)数据的容器;数组初始化=分配元素的内存空间+元素赋值(分配了空间则一定有了初始值,默认为对应类型的默认值);ArrayIndexOutOfBoundsException;参数非法IllegalArgumentException
= new 元素类型或子类[长度];//动态初始化:指定长度,由系统赋值
= new 元素类型或子类[]{元素1,元素2...};//静态初始化:赋初值,由系统决定长度;可简写:元素类型[] 变量名 = {元素1,元素2...}
List asList(arr) | 转为List集合,通过集合中的方法来遍历数组中的元素(不能增删元素,因为此List不是集合List及实现类(动态数组),而是Arrays的内部类,数组定长);转回数组(Collection接口的方法)list.toArray() |
int binarySearch(arr,[int fromIndex,int toIndex,]value) | (数据已升排序)二分搜索方式获得指定value的索引或负数(无value) |
type[] copyOf(originalArr,newLength) | 复制为新数组;newLength小时,截取originArr前面newLength长度的元素;newLength大时,后面以对应类型默认值补充 |
type[] copyOfRange(originalArr,int from,int to) | 复制数组中指定范围内的元素 |
equals(arr1,arr2) | 数组(元素)的比较 |
fill(arr,[fromIndex,toIndex],value) | 以指定value填充数组(的指定范围) |
sort(arr,[formIndex,toIndex]) | (指定范围内元素的)升排序;按字典顺序排序;先排序英文,再排序中文 自定义未实现接口Comparable的类的排序常报错ClassCastException:cannot be cast to java.long.Comparable;实现comparaTo()自定义对象比较规则 |
toString(arr) | 转为以,和空格隔开的一串长字符串 |
|
数组逆序; 字符串需要先转为字符数组:char[] chars = string.toCharArray();逆序后最后再toString(); |
自定义定义二分查找 :
public static int binarySearch(int[] arr,int value){
int min,max,mid;
min = 0;
max = arr.length - 1;
mid = (max+min) >> 1; //(max+min)/2;
while(arr[mid] != value){ //循环条件:遍历的mid处的元素不是需查找的值
if(value > arr[mid])
min = mid + 1;
else if(value < arr[mid])
max = mid - 1;
if(max < min)
return -1;
mid = (max + min) >> 1;
}
return mid;
}
-:隐藏对象的实现细节,通过公用方法暴露对象的功能;类是具有共同属性和方法的对象的抽象(化),系统最小执行单元
-:源文件中小于等于一个public类;如果有,源文件名必须与此类名相同;首行(非注释)表示文件所在的包
package packageName;
import packageName.*的*表示packageName包下所有类(而不会导入子包中的类);
默认的核心基础类java.lang.*无需导入;
静态导入:import static packageName.className.静态成员
-:常见包:
扩展类javax包;
java.util工具包;
java.net网络编程;
java.io输入/输出;
java.text格式化相关类;
java.sql数据库;
java.awt窗口工具;
java.swing图形用户界面
-:构造方法给对象进行初始化;不能static;显式声明无参构造方法(弹性);初始化动作只执行一次
-:值传递(传递实参的复制品,即方法中无论如何对复制品操作,实参不变)
实例swap():
1)main中定义两个变量a=1,b=2;方法swap(a,b)用来实现参数值交换;执行过程:
main栈入栈a=1;b=2
swap栈中入栈复制品并且被实参初始化a=1;b=2;交换后a=2;b=1;(方法中输出a=2,b=1);结束,栈消失
main栈中无影响即输出a=1,b=2
2)类Test中有两个字段a=1,b=2;方法swap(test)中实现test.a与test.b的值交换;执行过程:
main栈中入栈引用变量test,它指向堆中实例;赋值test.a=1,test.b=2即实例保存有字段a=1,b=2
swap栈开启,传递实参swap(test)即复制了一份test给swap,它也指向了堆实例;此时无论针对原test操作还是复制品test操作,实际操作的都是堆中实例,即实例的字段被交换了
将任何test=null,即引用变量不在引用实例,而实例中依然a=2,b=1;(当此实例是单例时,操作更是全局范围影响)
注意特殊的String:它不像普通类对象(针对一个对象的操作如duck的age),而是每新建一次都是一个新对象(sname="小备"即sname=new String("小备")),所以当输出name时输出"小飞",这类似值传递;但是注意区别类(类中String自为属性,后再方法中被改变)作为参数引用传递,依旧新建一个字符串就是一个新对象,但改变的是类对象指向的地址,所有还是同步更改。
A类需要直接访问B类中的成员,B类又需要建立A类的对象,则将A类(内部类)定义在B类中;内部类持有一个外部类的引用,实例化依赖外部类先实例化;而外部类想要访问内部类,必须先建立内部类的对象;内部类编译后的文件名为:“外部类名$内部类名.class”;同名变量用this.,外部类.this来区分
局部内部类(方法中定义的类)只能访问final修饰的成员
class Outer{
int num = 4;
//成员内部类
private class Inner {
void show(){ ... }
}
//提供内部类的实例化及逻辑实现
public void method(){
Inner in = new Inner();//创建内部类的对象。
in.show();//调用内部类的方法。
}
public void method2(){
//方法内部类;它访问局部变量时此局部变量必须final(JDK1.8之前)
final int x =0;
class Inner2{
void show2(){ ... }
}
//逻辑实现
Inner2 in2= new Inner2();
in2.show2();
}
}
内部类被视作外部类的成员(除了匿名内部类和局部内部类即外部类的方法中定义的类),任意权限修饰;静态内部类(不需要持有外部类的引用/对象,可单独实例化new Outer.inner())只能访问外部类的静态成员;内部类中定义了静态成员,该内部类必须是静态的;外部内只有public或default
不推荐的访问方式Outer.Inner in = new Outer.new Inner();内部类之所以定义在内部就是为了封装。想要获取内部类对象通常都通过外部类的方法来获取,对内部类对象进行控制:私有化内部类,外部类提供对外的方法,方法中实例化内部类并完成内部类逻辑处理
匿名内部类(一般在继承机制中):在new之后隐含一个接口或继承一个类;单例,没有构造器,不能定义静态成员和方法
final:提高性能即jvm和java应用会缓存final变量,优化final方法和类;安全的在多线程环境下实现数据共享而无需额外同步开销
不可变类Immutale:不能修改field,即只有getter()没有setter()
编译时类型(声明引用变量时指定的类型,左边)和运行时类型(由实际赋给变量的对象决定,右边)不一致现象
Animal a = new Cat();
编译时,a不能调用Cat自定义(即Animal中没有)的方法或field;运行时,判断调用父类(有)还是子类方法(重写的)
(强制)类型转换:数值类型之间或者继承关系类之间;否则ClassCastException
先判断类型:if(对象 instanceof 类型(如Student instanceof Person = true))
总原则:开放扩展性,关闭修改性(热插拔,即去扩展而不是更改代码功能)
(类的)单一职责 | |
里氏替换 | 任何能出现父类的地方都可以用子类替换 |
依赖倒转 | 面向接口编程,不是类之间而是上层接口之间的交互 |
接口隔离 | 接口中不存在子类用不到的方法,否则宁愿多声明接口 |
迪米特法则 | 最少知道原则,即不关心所依赖的类的逻辑,只需知道方法入口 |
合成复用 | 尽量少继承(is-a),多类的聚合(接口与实现类即has-a) |
一些工具类(只有方法没有属性)、频繁调用的类的设计 ,节省重复对象的创建
//饿汉式:
class Single{
private Single(){}//不让其他程序创建该类对象(本类构造器私有化即隐藏)
private static Single s = new Single();//在类封装时就实例化;缓存已经创建的对象(否则无法知道是否创建过对象,进而不确定对象唯一性);变量要被方法访问,所以static
public static Single getInstance(){//提供public方法供访问即Single s = Single.getInstance():静态化(因为可能还没有对象,需提供类调用方式)
return s;
}
}
//懒汉式:延迟加载方式;效率低,适合多线程环境
class Single2{
private Single2(){}
private static Single2 s = null;
public static Single2 getInstance(){
if(s==null)
s = new Single2();
return s;
}
}
//解决线程安全且通过双重(校验锁)判断提高效率:
//新建同步方法:
private synchronized static void getInit(){
if(s==null)
s=new Single2();
}
//然后getInstance()中判断后调用并返回
public static Single2 getInstance(){
if(s==null)
getInit();
return s;
}
//或者直接在方法中用同步代码块
public static Single2 getInstance(){
if(s == null){
synchronized(Single2.class){
if(s == null)
s = new Single2();
}
}
renturn s;
}
对简单工厂模式(基类是类,没法儿扩展只能修改)的改善;基类是接口:一个工厂接口+多个工厂实现类
Class Factor{
static FactClass create(int switch){
if(switch==1)
return new FactClass1();
else if(switch==2)
return new FactClass2();
}
}
FactClass factClass1/2 = Factor.create(1/2);//用工厂方法代替new
//如
Animal dog = new Dog();//Dog继承Animal;这种写法强耦合,产生依赖
//设计一个工厂模式,降低依赖
class AnimalFactory{
public static Animal getAnimal(String name){
if("dog".equals(name)){
return new Dog();
}else{ ... }
}
}
Animal dog = AnimalFactory.getAnimal("dog");
桥接模式:把事物和具体实现分开,用桥连接。DriverManager(数据库驱动)桥:连接数据库和JDBC提供的统一数据库连接接口
代理模式:静态代理指编译期已经写好代理类;动态代理指接口与实现类之间可以不直接关联而是在运行期间动态关联;继承的主要的两个类:
Object invoke(Object obj,Method method,Object[] args);//obj指代理类,method代理方法,args指方法参数列表
方法体中一般:method.invoke(proxy, args);//被代理对象调用invoke方法即交付代理类来执行
为对象提供一种代理,以控制对象的访问;(动态代理)如面向切面编程,@Autowired标注的属性即为被代理的对象,方法的前后在将来被添加一些额外操作(前置、后置增强)
模板方法设计模式:抽象类
装饰器设计模式:
装饰某个对象,在无需通过继承增加子类就能动态扩展(而继承是静态扩展)该对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀;典型如IO处理流
将类的接口转换成实际需要的适合的兼容的另一个接口;面向接口/抽象编程;接口常体现在参数传递上,所以是对方法重载的优化???
funciton(ImplementsA){...}//方法传递接口A类型参数,方法体中部分代码执行接口A的逻辑如它的一个方法actionA()
//现在需要传递接口B类型参数,方法体中除了要执行自己接口的逻辑actionB()外,其他代码都一样
//此时设计一个实现了接口A的适配器类(面向接口编程),接口B作为其属性并且通过构造器参数传递进来接口B;在actionA()方法中就可调用接口B的actionB()
//以有接口B为参数的构造器新建适配器类,调用funciton(适配器类)
迭代器模式:
提供一个方法/接口遍历集合而不关心集合内部元素;如Iterator
观察者模式:
一(主题对象)对多(观察者)的依赖关系:当主题对象的状态变化时,观察者被通知且自动更新(应用:系统广播)
boolean equals(Object obj) | ==:基本数据类型且数值型(不要求类型严格相同,即65与‘A’与65.0都返回true),值相同则返回true;引用类型变量:指向同一个对象则返回true;严格要求,即地址和内容都需要相同才true java设计中equals()的作用就是==;但实际需求可能需要引用类型变量的比较别那么严格(内容相同即可),所以重写equals()方法(如String类,要求字符序列相同即可) |
String toString() | 返回”类名@hashCode值”;为了对象对应的字符串内容有意义(自我描述),复写建立该类对象自己特有的字符串表现形式 |
Class getClass() | 对象运行时类(字节码文件对象) |
Object clone() | 自我克隆;与副本完全隔离;浅克隆(只复制基本数据类型;深拷贝:复制对象中的对象、数组等引用类型); 自定义:实现Cloneable接口及clone(),方法中调用super.clone() |
void finalize() | GC |
void wait()/notify()/notifyAll() | 线程的等待与唤醒 |
hashCode() |
PrintStream err标准错误流
InputStream in标准输入流
PrintStream out标准输出流
配合键盘输入/读取文件:
Scanner sc=new Scanner(System.in/new File(“filePath”));
int/long 等 input = sc.nextInt/Long()等; sc.hasNext();sc.next();
逐行读取:hasNextLine(), String nextLine()
long currentTimeMillis(); //1970年到当前时间(差)的毫秒值
exit(); // 退出虚拟机
Properties prop = System.getProperties(); //获取系统的属性信息并存储到Properties集合
System.setProperty(key,value);
gc();
Runtime类:java运行时环境;有通知系统垃圾回收、加载文件或动态链接库、访问JVM信息等方法
final类;私有构造器(不能建对象);方法全静态;一般返回值为double类型
PI,E
Random/ThreadLocalRandom:生成伪(相同种子产生相同随机数)随机数;构造器参数为种子(默认无参是当前时间;可自定义参数long);可生成浮点类型
current()获取ThreadLocalRandom对象;它是Random的增强版
rand.nextXXX()
NumberFormat类:
setMininumIntegerDigits()://整数部分允许的最小位数,不足则添0补齐
format()://数字格式化为字符串
格式化数据,如
SimpleDateFormat(与语言环境有关的方式格式化,如”yyyy-MM-dd”,即参数是日期模板);
String format(date);//格式化(转为字符串)一个日期/时间
Date parse(str);//根据字符串解析文本,生成日期;字符串不符合日期字符串的要求时ParseException
get/setCalendar();//获取-设置日历
对应setter(),参数重载
线程组对象ThreadGroup:批量控制线程
常量池:管理声明的常量(字符串是常量);无副本;一般规律:常量相加,直接连接即编译期优化,常量与变量相加,产生中间对象
String temp=”helloWorld”;
String s0 = “world”;// s0直接引用常量池中的”world”,被常量池管理(即直接引用)
String s1=”hello”;
String s2=”hello”+”World”;//s2引用的字符串可以在编译时期确定下来,被常量池管理,即s2==temp为true
String s3=s1+s0;//s3引用的字符串不能在编译时期确定下来,所以不被常量池管理,即s2==temp为false
//特殊的,如果s1和s0被修饰为final即常量替换,则在编译期直接被替换,可以确定下来,此时s2==temp为true
//方法返回值在运行期确定,即使方法返回值被赋给final
String s4 = new String("hello"); // s4指向是堆中新对象;两个对象”hello”和new;s4不与其他任何==;但s4.equals(s1)为true
String s5=s4;//s5==s4为true
String s5 = new String();//创建了包含0个字符的字符串(而不是null);一般不用new
String s = null/”hello”;//s没有指向任何对象,是一个null常量值
new String(char/byte[][,offset,length]);//将字符/字节数组(或部分)解析为字符串;同toString()
length();
new String(stringBuffer/stringBuilder):
String valueOf(…):
String str=”a”;//与
String str =new String(“a”);//的区别:
//前者等于调用String.valueOf()来返回实例,如:
public static String valueOf(int i){
return Integer.toString(i);
}
//后者调用构造器来赋值:
public String(String original){
this.value=original.value;
this.hash=original.hash;
}
==》查找:
char charAt(int index);//指定位置处的字符
int indexOf(char c[,int fronIndex]);//指定字符所在的位置;-1表示未找到,判断某一个字符是否存在
int indexOf(String str[,int fromIndex]);//指定字符串所在的位置
同理lastIndexOf():
子串:String substring(int start[,int end]);//start(闭)到length()-1或end(开)之间
==》判断:
boolean isEmpty();
boolean contains(String str);
boolean startsWith/ endsWith (char/string);
boolean equals[IgnoreCase](string);
int compareTo[IgnoreCase](string);//相同返回0;小于(参数)返回负数;大于返回正数(字符序列首次不同时的序号);特殊的如果一个是另一个从首字母开始的的子串,则返回长度差;
==》转换:
static String copyValueOf(char[][,int offset,int count] );
static String valueOf(char[][,int offset,int count]);
static String valueOf(char/Boolean/int/float/double/long/Object);
String toLowerCase/toUpperCase();
char[] toCharArray();//转为字符数组
byte[] getBytes();//转为字节数组
String[] split(分割的规则即正则表达式-字符串);//转成子字符串数组,切割
将一段文本切割为字符串数组,for(String str:strArr)可实现针对每个字符串的操作
==》内容替换:新字符串
boolean matches(string);
String replace[All][Firslt](oldChar/oldString,newChar/newString);
String concat(string2); //追加,拼接,同+
String trim();//去除首位空白
边界匹配:$行的结尾;^行的开头;\b字符边界;\B非字符边界;\A输入的开头;\G前一个匹配的结尾;\z或\Z输入的结尾
():圆括号表达式(常与|配合使用)
[]:方括号表达式
枚举:[abc]匹配abc中任意一个字符
范围:[a-f]匹配a-f内任意字符;[a-fh-y]匹配a-f和h-y内任意字符
求否:[^abc]匹配任意非abc字符;[^a-f]匹配任意非a-f内字符
与&&或并:表达式连接,如[a-z&&[^b]]匹配a-f内且不为b的字符
所以在[]中要匹配这些特殊字符,则需要转义,如\^,\-匹配^,-
{}:标记前面子表达式出现的次数;一个参数表示次数;一个参数和逗号表示最少次数;两个参数表示次数的范围;如\d匹配一个数字,\d{5}表示5个数字,\d{3,5}表示3-5个数字,默认贪婪模式(最大限度匹配即一直匹配直到不匹配或超出限度5;当?用在后面时,表示非贪婪模式,即在对应匹配规则前提下选择最少的匹配)
*:指定前面子表达式能出现的次数(至少0次,等价于{0,});如”zo*”匹配”z”或”zoo”
+:至少1次,等价于{1,};如”zo+”匹配”zo”或”zoo”
?: 0或1次,等价于{0,1};如”zo?”匹配”z”或”zo”
.:匹配除换行符\n之外的任何字符
\:转义下一个字符,或指定八/十六进制
|:或(二任选一)
匹配这些字符本身,则转义(忽略):如”\?\[“ 匹配?[
*:任何字符
\d:0-9数字digit;\D:非数字
如\d\d\d-\d\d\d:匹配任意数字形式如000-000
\s:所有空白(空格,制表,回车,换页,换行)space;\S:非空白
如a\sz:匹配如a空格z形式
\w:所有单词字符(0-9,英文字母和_)word;\W:非单词字符
如a\wz:配a字符z形式,如abz
Pattern:正则表达式编译后在内存的表示形式;即需要先编译为此对象p=Pattern.compile(string/”a*b”);建立正则表达式,注意Java中对\d等预定义的匹配符需要再用\转义一下\
Matcher:匹配的状态;m=p.mathcer(“a11111b”);boolean b=m.matches()//传入需要匹配的字符串,返回true;Pattern自己的方法Pattern.matches(“a*b”,”a111b”);返回true但每次都重新编译新对象;而m共享p对象
固定电话(首位0,2或3位后-,再接7或8位):0\d{2,3}-\d{7,8}
手机(首位1,2位为3或5或7或8或9,11位):1[35789]\d{9}
实例:字符串中字符的替换:
String str1 = "a235";
String str2 = str1.replaceAll("\\d","*");//将所有数字替换为*
//str2输出"a***";第一个\表示转义,\d表示匹配数字的正则表达式
字符串缓冲区(变量)容器,动态数组,解决字符串相加(变量+常量)带来的性能问题(中间垃圾对象);存储任意类型数据(最终需变成字符串):线程安全(排队);初始容量16;扩容2n;final类;toString()方法会将实例缓存,减少元素赋值开销;预知数据长度时建议使用带容量的构造器避免动态扩充次数
单线程下StringBuffer的简单替换,单线程下效率高;初始容量11字符,2倍+1;toString()直接返回新对象
StringBuffer append(data);//追加,data可以是容量、具体字符串、字符数组
StringBuffer insert(index,data);//指定位置增加
StringBuffer delete(start,end);//删除
StringBuffer deleteCharAt(index);//删除指定位置字符
StringBuffer replace(start,end,string);//替换
void setCharAt(index,char);//设置指定位置字符
void length()/setLength(len);//设置长度
查找方法同String
int capacity():
StringBuffer reverse();//反序
创建格式化对象:SimpleDateFormat format=new SimpleDateFormat(“yyyy-mm-dd”);
转为Date:Date date=format.parse(“日期时间字符串如2010-11-11”);
获取(当前时间的)Calendar对象:Calendar calendar=Calendar.getInstance();
转为Calendar对象:calendar.setTime(date);
获取年月日:year/month/day=calendar.get(calendar.YEAR/MONTH/DAY_OF_MONTH)+0/1/0
String string=year+”-“+month+”-“+day;
通过calendar.setTime(date)以后得到两个Calendar对象c1,c2;
获取时间值:long t1/t2=c1/c2.getTime[InMillis]();
相差毫秒值转天数:int betweenDays=(int)(t2-t1)/(1000*60*60*24);
只能存储引用数据类型(对基本数据类型装箱),可不同数据类型,打印时输出[…,…](重写了toString())
Collection接口:
|--List:有序(元素存入集合的顺序和取出的顺序一致);可重复
|--Set:
|--Queue:新集合,队列(FIFO);先进先出,队尾进/插入,队首出/删除
集合中元素使用equals()比较(不用==严格判断,即内容相同就认为对象相同)
boolean add(object);
boolean addAll(collection);
void clear();
boolean remove(obj) ;
boolean removeAll(collection) ;
boolean contains(obj) ;
boolean containsAll(collection);
boolean isEmpty();
int size();
boolean retainAll(collection) ;//对当前集合只保留与指定集合中所有相同的元素;如果两个集合所有元素相同,返回flase
Object[] toArray();
|--ArrayList:动态数组,替代Vector;查询快,增删元素慢因为需要移动数据;遍历和随机访问get(index)时间复杂度o(1),初始容量10,扩容一半;为防止动态扩充次数多影响性能,建议新建时指定初始容量
|--LinkedList:双向链表,适合增删操作,查询慢因为需要移动指针;实现了Deque接口(模拟栈LIFO和双端队列);查找的时间复杂度o(n);更占内存(每个节点存储了指向前/后元素的引用)
|--Vector:动态数组,线程同步,古老,查询和增删慢;10,扩容一倍
void ensureCapacity(int minCapacity);//增加数组长度
void trimToSize();//调整长度为元素个数,优化存储空间
方法变化:多了索引参数
void add(index,obj)/boolean addAll(index,collection)
Object remove(index) ;//返回被删的元素;失败会抛异常
Object get(index);
int indexOf/lastIndexOf(obj) ;
List subList(fromIndex,toIndex) ;
ListIterator listIterator();//获取所有元素;list集合特有的迭代器,同Iterator,用于集合的元素的增删查改操作
Object set(index,element) ;
LinkedList针对首尾元素特有的方法:
addFirst();addLast();offerFirst();offerLast()
getFirst/Last();//如果链表为空,抛出NoSuchElementException
peekFirst/Last();//JDK1.6;如果链表为空,返回null
removeFirst/Last();//NoSuchElementException
poll()/pollFirst/pollLast();//jdk1.6;如果链表为空即获取元素失败,返回null
|--HashSet:底层基于HashMap实现,哈希算法存储元素;唯一性(以及元素的操作)通过元素的hashCode()和equals()完成(hashCode()得到元素的hash值,相当于HashMap中的key,即equals()为true,表示相同元素,则value覆盖原来的元素而key不变;而如果hash值不同,则视为不同且存储在不同位置,而无需equals();存取查找性能高)
|--LinkedHashSet:HashSet+链表(用来维护元素次序,即有序的Set,但元素依然不能重复)
哈希(散列)表的原理:
1)对对象元素中的关键字(对象中的特有数据如field,为了提高效率,最好保证对象的关键字唯一),进行哈希算法运算,并得出具体的算法值(哈希值,int型)
2)哈希值表示这个元素的位置(所以快速定位);存储哈希值的结构即为哈希表;这类似数组的索引,但hash值不是连续的
3)如果hash值出现冲突(即相同),则equals()即判断这个关键字对应的对象是否相同。不同,就存储(同一位置存储多个元素,性能下降),在原来对象的哈希值基础 +1顺延
|--TreeSet:
常用类都实现了Comparator接口compareTo(obj):返回0表相同;正数表大于(所以实际也可以存储相同元素,只要让它的比较返回值总不是0);要求比较对象为相同数据类型,否则ClassCastException,所以TreeSet中元素一般同类型
Object first()/last():
Object lower(obj)/higher(obj):前一个/后一个元素
SortedSet subset(fromObj[,toObj]):
SortedSet tailSet(fromObj)/headset(toObj):指定元素之后(大于等于)的元素/之前(小于)的集合
排序需要依据元素自身具备比较性(常用于对象的判断如同姓名同性别但是否是同一个人);如果元素不具备比较性,在运行时会发生ClassCastException异常;所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法(按字典比较);依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置;唯一性通过参考比较方法的结果,如果return 0,视为两个对象重复,不存;或者实现Comparator接口比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造方法;第二种方式较为灵活
|--EnumSet:元素是枚举类型的枚举值
list元素去重:简单的当元素类型为基本数据类型时,与set(自动去除重复属性)互转:
list1.add(….); set.addAll(list); newList.addAll(set);
对于引用类型,list.add(new …);输出时确保实体类重写了toString();去重则须重写equals()和hashCode();
public boolean equals(Object args){
User u=(User)args;
return 实体类对象即this的属性.equals(u.属性)的并集;
}
public int hashCode(){
String str = 实体类属性连接;
return str.hashCode();
}
Map转为Set:set KeySet();set EntrySet();//Entry是Map内部接口,访问键值关系的入口
|--Hashtable:线程同步(安全);初始容量11,2n+1;不保证次序
|--HashMap:可以存储null键(唯一),null值;底层=数组+数组元素用单向链表实现的散列表;替代了Hashtable;16,扩容2n(即默认构造器,无参,加载/负载/散列因子折中为0.75;在超过容量*负载因子如16*0.75=12个元素时扩容);预设值8,表示当链表长度超过8的时候转为(平衡)二叉树结果而不是链表结构,否则深度太深,增删查太耗时;重载HashMap(int initialCapacity);HashMap(int initialCapacity,float loadFactory);加载因子表示Hash表中元素填满程度(太小则浪费空间,太高则增加元素冲突几率,查询成本增加);不保证次序;源码中Node作为内部类,属性int型hash,泛型key,value和代表下一个Node的next,数组即Node[];hash算法计算元素应该存放的位置(而不是简单的0-15随机数算法),通过key的key.hashCode()计算得到一个int值,值取模%16
|--LinkedHashMap:双向链表维护次序(记录了元素插入次序)(源码中双向链表的节点类Node有三个属性:data,next,pre)
|--TreeMap:类似TreeSet(父接口SortedMap);默认升序
key唯一,通过key计算hashCode()的hash值,值与数组长度取余,得到元素在HashMap中存储位置;值已存在(即余数相同,该位置有元素),则此位置元素以链表形式存放,新加入的元素在链头;优化则将hash值尽量分布均匀而不出现链表,节省遍历链表的时间和开销;当此key的equalse()返回true时,value被覆盖,否则表示不同元素,存储在链头;查找元素同理;判断两对象是否相等,也先判断hash值,相同后再equals();有时不严格判两对象相等(如自定义的类的属性相同即认为对象相同,存储时覆盖而不是添加),则(使用IDE默认重写规则)重写hashCode(),equals();重写equals()时,总应保证hashCode()或者ComparedTo(obj)有和equals()一致的结果
Object put(key,value);
void putAll(map);
void clear();
Object remove(key);
boolean isEmpty()
boolean containsKey(key);
boolean containsValue(value);
Object get(key);//键不存在则返回null (HashMap除外)
Collection values();//将所有value作为Collection
//获取Map中所有元素:
Set keySet = map.keySet();//将map的所有key作为set
Iterator it = keySet.iterator();//返回set的迭代对象
//如果确定集合中元素类型且类型统一,则Iterator<>较好如Iterator
//方法2:已知元素类型如String,则直接foreach遍历for(String string:keySet),或者String[] strings=keySet.toArray(new String[]{});
//当存在多种类型时:for(Object obj:keySet) { if(obj instanceof …){…} else if(obj instanceof …){…}};注意判断基本数据类型时是instanceof对应的包装类
while(it.hasNext()) {//遍历迭代对象
Object key = it.next();
Object value = map.get(key);
}
Set entrySet = map.entrySet();//将key-value对作为set
Iterator it = entrySet.iterator();
while(it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();//Entry是Map内部类,即元素数组(key-value)是它的对象,持有指向下一个元素的引用
System.out.println(me.getKey()+"::::"+me.getValue());
}
//遍历键值对
Set> entrySet=map.entrySet();
for (Map.Entry entry:entrySet){
System.out.println(entry.getKey()+"->"+entry.getValue());
}
//遍历键,通过键得到值
Set keys=map.keySet();
for (Integer integer:keys){
String value=map.get(integer);
System.out.println(integer+"=>"+value);
}
//遍历值
Collection values=map.values();
for (String value:values){
System.out.println(value);
}
void sort(list[,comparator]);//元素的自然顺序排序/按指定规则排序
void swap(list,i,j);
Object min/max(collection);
int binarySearch(list,key);
void reverse(list);
void shuffle(list);//随机排序
void fill(list,obj);
synchronizedXxx();//包装集合使其线程安全;Xxx表示各种集合,参数为集合对象
emptyXxx()/singletonXxx()/unmodifiableXxx();//设置集合不可变,只读
rotate(list, distance);//旋转,参数2表示移动个数
Iterator it = collection.iterator();//接口,线程安全;容器都有迭代器对象;或foreach循环;List集合还可以根据索引使用普通for循环
List额外提供的方法:ListIterator listIt=list.listIterator()
ListIterator(继承自Iterator)扩展的方法(向前迭代功能和添加元素):
boolean hasPrevious();
Object previous();
void add();
boolean hasNext();
Object next();
void remove();
多线程对同一集合操作时,使用快速失败(fail-fast)机制,即遍历过程中只能以此方法操作;不能使用集合或其他线程的操作,否则ConcurrentModificationException
最佳实践:不建议同一个集合中存储多种类型的数据,即最好使用泛型来限定类如List
集合中对象的一对多与多对多关系:
多的一方作为属性如一个老师对多个学生:private HashSet
多对多关系通常拆分为两个一对多,即产生中间表/类
封装程序在运行时出现的不正常情况;解决程序无法掌控但又需要的部分;容错,健壮
java.lang.Throwable(可抛出的)
|--Error:
|--Exception:有针对性的处理方式
1)抛出:功能内部处理不了,就必须抛出,让调用者处理;throws 可能的异常名列表,用在方法上;throw一定排除异常,用在方法体
2)捕捉:内部可以处理的
try { 需要被检测的代码;}
catch(异常类 变量名){ 异常处理代码(异常的抛出与捕获);e.printStackTrace();//打印异常在堆栈中信息;异常名称+异常信息+异常的位置 };可多个,try中出现的异常匹配catch列表中(从上到下查找)参数指定的异常类型后执行catch,都不匹配则抛出,由系统报错提示;catch中如果抛出异常(如throw new ArithmeticException("除数不能为0")),则表示发生异常,退出系统,没有return
finally{ 异常处理的出口; 资源回收与关闭;一定执行;除非System.exit(0); //退出jvm}
catch和finally至少有出现其中一个;在执行return语句之前总先执行finally(所以一般finally中避免return或throw等导致方法终止的语句);try,catch,finally三个代码块中的变量作用域独立且不能互相访问
运行时异常UncheckedExceptions,编译时不被检查,特殊子类RuntimeException
自定义异常(如定义除数还不能为负数):继承Exception或RuntimeException,让该类具备可抛性,然后通过throw 或者throws进行操作(查看已经定义的异常类,发现实际就包含了两个构造方法,方法体调用父类,可参照它们写)
继承中重写的方法中的异常处理:父类的方法抛出异常,则子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类或子集;父类或者接口中的方法没有抛出过异常,则子类不可以抛出异常,只能自己处理try
RuntimeException:
IndexOutOfBoundsException:
NullPointerException:
ArithmeticException:除数为0
ArrayStoreException:数组存储空间
ClassCastException:
NegativeArraySizeException:数组长度是负数
OutofMemoryException:
IllegalAccessExcePtion:试图访问一个非public方法即安全权限异常,反射时调用了private方法
NumberFormatException:
InstantiationException:对象实例化异常
InvocationTargetException:调用的方法内部抛出异常而未捕获时,由此异常接收
CheckedException:
IOException:由于文件未找到、未打开或者I/O操作不能进行而引起异常
FileNotFoundException
ClassNotFoundException
NoSuchMethodException
SQLException:
InterruptedException:当一个线程处于等待状态时,另一个线程中断此线程
ServletException:
线程是进程中的程序执行控制单元;每个线程在栈区中都有自己的执行空间,方法区和变量;共享进程资源;抢占式执行;一个线程可创建和撤销另一线程;jvm启动时,主线程main方法负责程序的执行;GC是另一个线程
用户线程(JVM在没有用户线程运行时关闭程序和退出,如主线程);守护线程(后台执行如GC,在没有用户线程时自动死亡)
继承Thread类(各个线程分别完成自己的任务,不共享线程中的实例属性)或实现Runnabe接口(作为Thread的target;改善单继承的局限性,多个线程共同完成一个任务,共享线程中的实例属性;推荐,因为java提倡少继承多组合),复写run方法;创建线程对象,调用start()开启线程
JDK1.5可以使用Callable接口实现call()创建线程并获得线程返回值
线程状态/同步/调度相关方法/传统线程通信/生命周期:new – start() – 运行(获得CPU使用权和时间) – 阻塞block(wait()/sleep()(主动放弃占用的CPU资源,指定时间过后进入就绪)等) – 死亡(stop(),或者run()/call()方式执行完,或者线程抛出未捕获异常)
wait()/notify()/notifyAll():Object类的方法(因为与对象的锁关联);只能用在同步区域(因为与锁关联);释放锁,进入等待(被唤醒)池;notify()仅随机唤醒一个处于wait的线程;;wait()线程冻结即释放了执行权,释放了资格;将线程对象存储到线程池;可不指定时间
start()之后立即执行,可Thead.sleep(1)让主线程/当前正在执行线程睡眠
线程优化:
线程池ThreadPool + 程序计数器CountDownLatch
程序启动时就创建若干(工作)线程以供使用,降低资源(创建和销毁)消耗,重复利用,提高响应速度(无需等待线程创建),提高线程管理
适用于频繁操作但任务时间短的情况如网页点击,任务被添加到队列,在有空闲线程使调度任务给他;
线程池
线程池管理器ThreadPoolManager |
创建线程池并管理 |
|
工作线程WorkThread |
class WorkThread extends Thread{} |
|
任务接口Task |
任务实现的接口被工作线程调用 |
|
任务队列 |
缓冲未处理任务 |
java线程池
接口:Executor,ExecutorService,ScheduleExecutorService(具有定时功能) 类:Executors,AbstractExcutorsService,ThreadPoolExecutor,ScheduledThreadPoolExecutor |
||
构造器参数含义(调优参考) |
corePoolSize,maximumPoolSize:最少/多线程数;如果池中数量过小,则即使线程空闲,也创建新线程来处理被添加任务;如果大于等于,而队列未满,则任务放入队列缓冲 |
|
keepAliveTime:线程允许最长空闲时间;过时则线程停止,从而动态调整池 |
||
workQueue:任务线程队列;排队的三种策略:直接提交,无界、有界队列 blockingQuere:带锁的阻塞队列,其下又分为基于数组、链表、同步、优先权的队列 |
||
threadFacroty:线程工厂;defaultThreadFactory创建同线程组且默认优先级线程;privilegedTheadFactory创建访问权限控制线程 |
||
handler:超出线程范围或队列容量后的4种可选处理策略:抛出异常,重试添加任务,放弃旧任务,抛弃任务 |
||
方法含义execute(Runable):添加任务到池 |
||
java通过Executors提供四种线程池 |
||
newCachedThreadPool可缓存;线程数超过任务数时回收空闲线程,任务增加时自动增加线程数 |
||
newFixedThreadPool 定长,可控制线程最大并发数,超出的线程会在队列中等待 |
ExeutorService pool = Executors.newFixedThreadPool(2); Thread t1,2,3=new MyThread(); pool.execute(t1,2,3);//放入线程池并执行 pool.shutdown();//关闭线程池 |
|
newScheduledThreadPool定时及周期性执行,无限大小 |
||
newSingleThreadExecutor 单线程化,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行 |
||
主线程调用wait()使主线程阻塞;计数器做参数传给线程,执行完后调用countDown()表示完成任务,
Fork/Join框架
JDK8并行流:顺序执行的流编程并发的流
多线程性能问题及避免和改善:
1死锁:
尽量不要在释放锁之前竞争其他锁,细化同步方法,只在真正需要保护共享资源的地方去拿锁,并尽快释放锁
如果无法避免嵌套索取锁资源,则需要制定一个索取锁资源的策略,先规划好有哪些锁,然后各个线程按照一个顺序去索取
尝试定时锁即显式地索取和释放锁,设定一个超时时间,如果超过这个时间还没索取到锁,则不会继续堵塞而是放弃此次任务
JVM采用thread dump的方式来识别死锁的方式,通过操作系统的命令来向JVM发送thread dump的信号,查询哪些线程死锁
2过多串行化
3过多锁竞争
缩小锁的范围,尽量不要直接在方法上使用synchronized关键字,而只是在真正需要线程安全保护的地方使用,快进快出
减小锁的粒度,synchronized关键字(用在方法上)是默认把整个对象作为锁,没必要用这么大一个锁,这会导致这个类所有synchronized都得串行执行。可以根据真正需要保护的共享变量作为锁,也可以使用更为精细的策略
减少共享资源的依赖
读写分离锁来替换独占锁,读写分离锁(ReadWriteLock)来实现读-读并发,读-写串行,写-写串行的特性
4切换上下文
5内存同步
解决多(并发)线程安全问题:将操作共享数据的语句在某一时段让一个线程执行,其他线程不能进来执行,即同步代码块/监视器:
synchronized(锁) { //同步普通方法,则以当前对象实例为锁;同步静态方法,以类的Class对象为锁;同步静态代码块,锁为括号内容对象
需要被同步的代码;
}
同步方法/代码块结束,return/break,抛出未处理异常,和wait()会释放锁
//互斥锁:灵活的同步锁来控制同步,lock()加锁,unlock()释放锁, Lock lock=new ReentrantLock();
悲观锁:总是假设最坏的情况,每次操作时都认为别的线程会修改数据,所以先上锁让其他线程阻塞直到释放锁给其他线程
乐观锁:总是假设最好的情况,每次操作时都认为别的线程不会修改数据,所以不会上锁;但在更新前会判断在此期间其他线程是否更新数据;可以使用版本号机制和CAS算法实现
死锁:多个线程的阻塞(资源短缺,暂停执行,等待对方释放锁);不抛出异常也不继续执行;进程死锁4个必要条件(解决死锁:破坏任意条件,合理分配资源):
互斥:进程独占资源且排他使用(任意时刻资源只能被一个进程使用)
不可剥夺:正在使用的资源除非自己释放,不被其他进程强行剥夺
请求和保持:进程请求资源,同时保持占用已经分到的资源
循环等待:前一个进程占有后一个进程请求的资源
//生产者-消费者问题
class Containner{
final int max=10;int index=0;
synchronized void put()/get(){
while(index==max/0){
try{
wait(); //如果简单模拟一个线程j++,一个j--,则不会出现阻塞,即不必wait()和notify()
}
catch(InterruptedException e){ }
finally{}
}
index++; //当index!=max时操作;否则等待
//index--; 当index!=0时操作,否则等待
notifyAll(); //唤起
}
//或者不用循环而用判断:
//if(index0)
//{ index++/--; notifyAll(); }
//else wait();
//然后在线程的run()方法中用循环:
//while(true){
//Thread.sleep(); put()/get();
//}
//生产与消费线程
class Producer/Consumer implements Runnable{
Containner c;
构造器(Container c){
this.c=c;
}
@override
public void run(){
//for(i<20) 模拟调用20次;并休眠
c.put()/get();
try{
Thread.sleep(1000);
}…
}
}
new Thread(new Producer(new Containner)).start(); //n个线程
线程局部变量:
spring通过各种模板类(线程安全)降低数据持久技术难度;synchronized,时间换空间,降低并发性;于是spring使用ThreadLocal来管理request作用域中的bean/事务管理/任务调度/AOP等
互斥同步是多线程间的数据共享,线程局部变量是线程间的数据隔离,即ThreadLocal把共享数据的可见范围限制在同一个线程之内
java.lang.ThreadLoacal:为每个使用该变量的线程分配独立的变量副本,各自独立操作;适用于资源共享但不需维护(资源修改但不影响另一个线程的运行,空间换时间);
get()/set();initiaValue()赋初始值;remove()移除值
进程:
独立,动态(系统中活动的指令集合,程序是静态的指令集合),并发(同一CPU上宏观多个进程同时执行;并行是同时在不同CPU上执行相同进程)
进程之间通信:
管道(半双工模式,数据单向流动,适用于父子关系的进程;有名管道可无此限制)
信号量:计数器,标记正在访问资源的进程,防止其他进程来访问
消息队列;信号;共享内存
套接字:不同机器上的进程也能通信
进程调度算法:
实时系统:FIFO先进先出算法,SJF最短作业优先
交互式系统:RR时间片轮转,HPF最高优先级,最短进程优先,公平分享调度,保证调度;区别于线程调度器:为可运行状态的线程分配CPU时间(分配的过程叫时间分片)
给线程命名,方便调试;同步范围最小化(只对关键代码同步);使用更高层次的并发工具如BlockingQueue(而不是wait()/notify()来线程通信);使用线程池(系统启动时创建大量空闲线程,启动线程后赋给线程池,结束后池中该线程又变空闲);
同步:
互斥(同时刻只能一个线程持有锁)和可见性(先前线程对数据的操作对后来持有相同锁的线程来说可见)
等待-唤醒机制:线程的方法组合,只能用于同步中(因为方法需要标识所属的锁)
停止线程:结束run方法(一般run方法里肯定定义循环,所以结束循环即可。定义循环的结束标记或者,如果线程处于冻结状态,是不可能读到标记的,此时通过Thread类中的interrupt方法,将其冻结状态强制清除,让线程恢复具备执行资格的状态,让线程可以读到标记,并结束)