一、基本数据类型
1、整型
- byte:1字节(-2 的 7 次方到 2 的 7 次方-1)
- short:2字节(-2 的 15 次方到 2 的 15 次方-1)
+=、-=、*=、/=、%=自带强转功能 - int:4字节(-2 的 31 次方到 2 的 31 次方-1)
- 整数直接量默认为int型,超范围则编译错误(-21个多亿到21个多亿)
- 整数相除,小数位无条件舍弃
- 整数运算时超出范围,则发生溢出,溢出需要避免
- long:8字节(-2 的 63 次方到 2 的 63 次方-1)
- 长整型直接量需在数字后加L或l
- 运算时若有可能溢出,建议在第1个数字后加L
- System.currentTimeMillis()用于获取自1970.1.1零时到此时此刻的毫秒数
2、浮点型
- float:4字节
- float需在数字后加f或F
- double:8字节
- 浮点型直接量默认为double型
- double和float型数据运算时,有可能会出现舍入误差,所以精确运算场合不能用,精确运算建议用BigDecimal
3、逻辑型
- boolean:1字节
- 默认为false
4、字符型
- char:2字节
- 采用Unicode字符集编码
- 表现形式是char,实际存储是int(0-65535)
- ASCII码: ‘a’-97 ‘A’-65 ‘0’-48
- 字符直接量必须放在单引号中,只能有一个
- 特殊符号需要 \ 转义
二、switch(expr)
- 早期的JDK,expr可以是byte、short、char、int
- JDK1.5后,expr可以是enum
- JDK1.7后,expr可以是String,不可以是long
三、数组
- 数组
- 是一种数据类型(引用类型),也是一种对象
- 数组大小不能任意改变
- 不属于原生类 (原生类是一种基本数据类型)
- 初始化
- int[] arr = new int[4];
- int[] arr = {1,2,3,4};
- int[] arr = new int[]{1,2,3,4};
- int[] arr; arr = {1,2,3,4}; //编译错误,此方式只能声明的同时初始化
- arr = new int[]{1,2,3,4}; //正确
- 复制
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
灵活性好,效率高Arrays.copyOf(arr, length);
// 数组的扩容
灵活性差,效率低,但是可以扩容
Arrays.sort(arr);
排序
四、变量
由字母、数字、_、$组成,不能以数字开头
- 成员变量
- 实例变量
- 无static修饰,有默认值
- 属于对象的,存储在堆中,通过对象点来访问,对象被回收时一并被回收
- 静态变量
- 由static修饰
- 属于类的,存储在方法区中,通过类名点来访问
- 实例变量
- 局部变量
- 调用方法时存储在栈中,方法结束栈帧被清除时一并被清除
- 可以与成员变量同名,使用时采取就近原则
五、final(最终不可改变)
JDK中的一些基础类库被定义为final,如String、Math、Integer、Double等等。
- 修饰变量:变量不能被改变
- 编译期常量
类加载时完成初始化,编译后带入到任何计算式中,只能是基本类型 - 运行期常量
基本数据类型或引用数据类型,引用不可变,但引用的对象内容可变 - 成员变量与局部变量区别
修饰成员变量,可以在声明的同时初始化,或在构造方法中初始化
修饰局部变量,只要在用之前初始化即可
- 编译期常量
- 修饰方法:方法不能被重写
- 修饰类:类不能被继承
- 好处
- 提高性能
jvm和Java应用都会缓存final变量 - 可共享
可以在安全的多线程环境下进行共享,不需要额外的同步开销 - jvm会对方法、变量和类进行优化
- 提高性能
- final static(常量)
- 必须声明同时初始化
- 通过类名点来访问,常量不能被改变
- 编译器在编译时将常量自动替换为具体的值
- 何时用:数据经常使用且永远不变
六、static(静态)
- 静态变量
- 属于类的,存储在方法区中,只有一份
- 常常通过类名点来访问(不建议通过对象名访问)
- 静态方法
- 属于类的,存储在方法区中,只有一份
- 静态方法不能直接访问实例成员,需要用对象点来访问
静态方法没有隐式的this传递,就没有对象 - static方法不能被abstract修饰,不能被覆盖,因为static是编译时静态绑定的,而方法重写是运行时动态绑定的。
- 静态块
- 属于类的,在类被加载期间自动执行,只执行一次
- 执行顺序
父类静态代码块 --> 子类静态代码块 --> 父类非静态代码块 --> 父类构造方法 --> 子类非静态代码块 --> 子类构造方法
七、访问控制修饰符
类的访问修饰符只能是public或默认的
任何类 | 父子类 | 同包类 | 本类 | |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
default | √ | √ | ||
private | √ |
八、面向对象
- 封装
- 类: 封装对象的属性和行为
- 方法: 封装业务逻辑功能
- 访问控制修饰符: 封装具体的访问权限
- 继承
- 作用:代码复用
- 特点:单继承,传递性,多接口实现
- 优点
- 子类能自动继承父类的接口
- 创建子类对象时,无须创建父类的对象
- 缺点
- 子类与父类具有耦合,子类依赖于父类的实现
- 增加系统结构复杂度
- 不支持动态继承(在运行时,子类无法选择不同的父类)
- 子类不能改变父类的接口
- 多态
- 行为多态(所有抽象方法)、对象多态(所有对象)
- 向上造型、强制类型转换(想访问派生类所特有的)、instanceof判断
- 多态的表现形式
- 重写:根据对象的不同来多态
- 重载:根据参数的不同来多态
九、包装类
1、存在意义
Java是一个面向对象编程的语言,而基本类型不具有对象的性质,引入包装类可以使基本类型具有对象的性质。比如在使用Collection集合类型时一定要使用包装类型,使其具有对象的性质,且为其添加了属性和方法,方便操作。
- 可以参与面向对象编程
- 解决不能参与面向对象编程 (多态)
2、特点
- 包装类被final修饰,不可变类,一旦构造对象就不可改变
- 六种包装类继承Number,Number是抽象类(八种基本数据类型中的包装类,除Character和Boolean继承于Object类)
- 这六种包装的数据可以相互转换 (在Number中有相互转换的方法)
3、自动拆装箱
自动拆装箱特性始于JDK1.5版本,编译器会自动添加代码
- 自动装箱(基本数据类型–>包装类型)
- 给直接量的范围在-128~127,会有对象重用,如果不在这个范围会创建对象
- 缓冲的值一旦超过范围就会强制new对象
- Integer i = 100; // 编译器会改为new Integer(100); 底层调用Integer.valueOf(100)方法实现;
- 自动拆箱(包装类型–>基本数据类型)
- int i = new Integer(6); // 底层调用i.intValue();方法实现
4、总结==
- 两个通过new生成的Integer变量永远是不相等的
Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false
- Integer变量和int变量比较时,只要两个变量的值是相等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true
- 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(100); Integer j = 100; System.out.print(i == j); //false
- 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true
Integer i = 100; Integer j = 100; System.out.print(i == j); //true
- 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值不在此区间,则比较结果为false
java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);Integer i = 128; Integer j = 128; System.out.print(i == j); //false
public static Integer valueOf(int i){ assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high){ return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); }
5、Int和integer的区别
- 类型不一样
int是java的8种基本数据类型之一。
Integer是Java为int类型提供的包装类。 - 默认值不一样
int变量的默认值为0。
Integer变量的默认值为null。(Integer可以区分出未赋值和值为0的区别)
比如:学生A没来参加考试,学生B参加考试但是都答错了,则学生A的成绩是null,学生B的成绩是0分。
十、字符串
1、String介绍
- java.lang.String被final修饰,不能被继承
- 字符串一旦创建,对象永远无法改变,但字符串引用可以重新赋值
- Java字符串在内存中采用Unicode编码,一个字符对应两个字节的定长编码
- 频繁修改字符串就不要使用String
2、String相关方法
- length() 获取长度
- indexOf 检索
- int indexOf(String str): 返回第一次str出现的位置,找不到返回-1
- int indexOf(String str, int fromIndex): 从字符串的fromIndex位置开始检索
- int lastIndexOf(String str, int fromIndex): 返回最后一个出现的位置
- substring 获取子串
- String substring(int beginIndex): 从下标beginIndex开始到字符串结尾的子字符串
- String substring(int beginIndex, int endIndex): 包头不包尾
- toUpperCase、toUpperCase 大小写转换
- valueOf 将其他类型转换为字符串类型
- split 以给定字符进行截取
- format 字符串格式
- str=“123+456”; String[]s = str.split("\+");
- trim去掉两端的空字符
- charAt 返回字符串指定位置的字符
- startsWith、endsWith 检测字符串是否以指定字符串开头、结尾
3、String&StringBuffer&StringBuilder
String | StringBuffer | StringBuilder | |
---|---|---|---|
相同点 | 都是final类,不允许继承 | ||
区别 | 长度不可变 | 长度可变 | |
-- | 线程安全、同步处理、性能稍慢 | 非线程安全、并发处理、性能更好 |
4、String
- 因为String不能被继承,通常使用StringUtils、ObjectUtils来增强String的功能。
- String内部重写了hashCode和equals。
- substring
会创建新字符串; 新字符串是在运行时在堆里创建的。 - String str = “ABC”;
可能创建一个对象或者不创建对象。
如果“ABC”在String常量池里不存在,则会创建一个String对象“ABC”,然后str指向这个内存地址;
如果存在,则不创建对象,指向已存在内存地址,之后都是String的拷贝,Java中称为“字符串驻留”,所有字符串常量都会在编译之后自动驻留。 - String str = new String(“ABC”)
至少创建一个对象,也可能两个。
因为new,肯定会在堆中创建一个str对象,它的value值是“ABC”,如果这个“ABC”在字符串常量池里不存在,会创建“ABC”对象。
String s1= “a”; String s2 = “a”; 此时 s1 == s2 返回 true
String s1= new String(“a”); String s2 = new String(“a”); 此时 s1 == s2 返回 false - final
final会在编译器被优化,且被直接运算好。- String为什么被final修饰?
如果String不是final,那么子类就可以继承String类,然后子类可以重写其方法,这些重写的方法可以修改字符串,就违背了String的不可变性。 - 不可变原因
- 安全
字符串常常用来表示url,文件路径,如果可变,会存在安全隐患。 - 提高效率
比如String str1 = “abc”; “abc”放到常量池里。String str2 = “abc”; str2的“abc”不会复制,只会多个引用指向原来的常量,这样就提高了效率。前提是String不可变,如果可变,多个引用指向同一个字符串,那么一个引用改变字符串,则其他引用也被影响。 - String不可变,hashCode就一样,不用每次重新计算
- 安全
- String为什么被final修饰?
例子
/**
* final在编译器被优化,且会被直接运算好
*/
public class TestAwen {
public static final String a1 = "a";
public static String a2 = "a";
public static void main(String[] args) {
String a = "a";
final String b = "b";
final String c = a + b;
String d = a + b;
String e = a + "b";
String f = "a" + b;
String g = "a" + "b";
String h = "ab";
String i = new String(h);
String j = a1 + b;
String k = a2 + b;
System.out.println(c == h); // false
System.out.println(d == h); // false
System.out.println(e == h); // false
// f、g、h在编译时优化,运算为字符串常量ab
System.out.println(f == h); // true
System.out.println(g == h); // true
System.out.println(i == h); // false
System.out.println(j == h); // true
System.out.println(k == h); // false
}
}
- 用“+”来拼接字符串时,底层会通过StringBuilder实例的append()方法来实现。
- String intern(); 重用String对象
- JDK1.7之前,字符串常量池放入方法区;
- 自从JDK1.7开始,字符串常量池放入堆中;
- 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 确定),则返回池中的字符串。否则,常量池中直接存储堆中该字符串的引用(1.7 之前是常量池中再保存一份该字符串)。
- 源码:public native String intern();
- 例1
String s = new String("1"); // 常量池中生成“1”,堆中创建字符串对象 s.intern(); // s对象去常量池中找,发现“1”已经存在与常量池中 String s2 = "1"; // s2指向常量池中的“1” System.out.println(s == s2);// false // s指向堆中的字符串对象,s2指向常量池中的“1”,两者引用地址明显不同 String s3 = new String("1") + new String("1"); // 常量池中生成“1”;堆中生成s3引用指向的对象(内容为“11”),但是此时常量池中没有“11” s3.intern(); // 将s3中的“11”放入常量池中,此时常量池中不存在“11”字符串,JDK1.6是直接在常量池中生成一个“11”对象;JDK1.7直接存储堆中的引用 String s4 = "11"; // 去常量池中找,发现已经存在,则指向s3引用的对象 System.out.println(s3 == s4);// true
- 例2
String s3 = new String("1") + new String("1"); // 常量池中生成“1”;堆中生成s3引用指向的对象(内容为“11”),但是此时常量池中没有“11” String s4 = "11"; // 去常量池中找“11”,发现没有则生成“11” s3.intern(); // 去常量池中找“11”,发现已经有了 System.out.println(s3 == s4);// false
- 例3
String str1 = new String("SEU") + new String("Calvin"); System.out.println(str1.intern() == str1);// true // str1.intern()发现常量池中不存在“SEUCalvin”,因此指向了 str1 System.out.println(str1 == "SEUCalvin");// true // "SEUCalvin"在常量池中创建时,也就直接指向了 str1 了
- 例4
String str2 = "SEUCalvin";//新加的一行代码,其余不变 String str1 = new String("SEU") + new String("Calvin"); System.out.println(str1.intern() == str1);// false // str2 先在常量池中创建了“SEUCalvin”,然后str1.intern()就直接指向了 str2 System.out.println(str1 == "SEUCalvin");// false // 所以谁都不搭理在堆空间中的 str1 了
- hashCode和equals
- 当equals被重写时,必须重写hashCode方法,以维护hashCode方法的常规规定(规定声明两个相同的对象必须有相同的hashCode);
- 当equals为true时,hashCode为true;
- 当hashCode为false时,equals为false;
- 当hashCode为true时,equals不一定为true;
- 如果重写equals而不重写hashCode
- 比如存储散列集合时(Set),如果只重写了equals,没有重写hashCode,则在集合中将会存储两个值相同的对象,从而导致混淆。
- 当equals被重写时,必须重写hashCode方法,以维护hashCode方法的常规规定(规定声明两个相同的对象必须有相同的hashCode);
- 追加、插入、替换的方法返回结果是this,toString方法返回的是String
- 有没有哪种情况用+做字符串连接比调用StringBuffer / StringBuilder对象的append方法性能更好?
如果连接后得到的字符串在静态存储区中是早已存在的,那么用+做字符串连接是优于StringBuffer / StringBuilder的append方法的。 - 字符串的==与equals
String s1 = "HelloWorld";
String s2 = new String("HelloWorld");
String s3 = "Hello"+"World";
System.out.println(s1==s2); // false
System.out.println(s1==s3); // true
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
System.out.println(s1.intern()==s2.intern()); // true
StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
s1.equals(s2) //是false // 因为StringBuffer类中没有重新定义equals这个方法,因此这个方法就来自Object类
5、StringBuffer & StringBuilder
- StringBuffer 和 StringBuilder中所有方法都相同。
- StringBuffer 在 StringBuilder的方法上添加了synchronized,保证线程安全。
- StringBuffer:线程安全、同步处理、性能稍慢
- StringBuilder:非线程安全、并发处理、性能更好
- 注意:StringBuffer和StringBuilder内部没有重新定义equals方法,因此用equals比较值是否相等时,结果为false
十一、正则表达式
- 正则表达式
是一组特殊的字符,描述一个字符串格式
对字符串可以进行复杂操作
不关心内容是否有效,只关心格式是否正确 - 作用: 匹配字符串是否满足给定格式
- 字符集合
[ ] 表示一个字符
[123] 表示123中任意一个字符
[0-9] 表示0~9中任意一个字符
[ ^123 ] 除了123的任意字符
[a-z&&[ ^b ]] a~z中除了bc以外的任意一个字符 - 数量词
x? 表示0个或1个x
x* 表示0个或任意多个x
x+ 表示1个到任意多个x
x{n} 表示n个x
x{n, } 表示n个到任意多个x
x{n, m} 表示n个到m个x - 预定义
. 任意一个字符
\d 任意一个数字字符,相当于[0-9]
\w 单词字符,相当于[a-zA-Z0-9_]
\s 空白字符
\D 非数字字符
\W 非单词字符
\S 非空白字符 - 分组( ): 将一系列正则表达式看作整体,分组与分组直接可以用"|"表示或
- “^“和”$” 表示字符串开始和结束,不写方法内部自动添加
- 方法
boolean matches(String regex) 将一个字符串与正则表达式进行匹配
String[] split(String regex) 将字符串拆分成字符串数组
String replaceAll(String regex, String replacement) 将字符串中匹配正则表达式regex的字符串替换成replacement - 常用正则
- Email地址:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
- 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
- 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
- 身份证号(15位、18位数字):^\d{15}|\d{18}$
- 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
- 帐号是否合法(字母开头,允许8-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{7,15}$
十二、Object
1. 所有类的超类
直接继承: 创建class后没有继承任何类,在Java编译器编译的时候,自动添加关键字extends,继承Object类
间接继承: 这个类继承了另一个类,类外的类间接或直接继承Object
2. String toString()
没有重写输出的是地址值 (当前类继承Object类,Object类中输出的是当前对象的地址值)
重写后输出的给定的内容
3. boolean equals()
equals没有重写比较地址值,重写后比较内容、= =是比较值是否相等
String是final类,已经重写了toString方法
= =比较是否是一个对象 (可能值相等,但不是一个对象)
equals比较值是否相等
十三、日期操作
1. Date
java.util.Date
Date的每一个实例用来表示一个确切的时间点; 内部(对象)维护了一个long值,距1970.1.1的毫秒值;
正数是1970年以后的日期,负数是1970年以前的日期;
由于Date设计上存在缺陷(时区、千年虫),导致大部分方法设置为“过时的”(不建议使用)。
toString()
格式: Sat Sep 08 16:13:35 CST 2018;
Date重写了toString,返回的字符串显示的是当前Date表示的时间;
long getTime()
获取Date内部维护的long值
void setTime(long)
设置一个long值,使Date表示该long值所表示的时间
大部分方法已过时(eg: getYear()、getMonth()等)
2. SimpleDateFormat
java.text.SimpleDateFormat
根据给定日期格式将String与Date相互转换: Date–>String 格式化、String–>Date 解析
日期格式
y-年、M-月、d-日 写M,如果表示两位数的月份,最后还是会自动显示两位月份
E-星期、a-AM或PM标识
H-小时(24)、h-小时(12)、m-分钟、s-秒
构造方法
SimpleDateFormat()
SimpleDateFormat(String pattern)
用给定模式和默认语言环境的日期格式符号构造
String format(Date date) Date–>String
将给定的Date,转换为SimpleDateFormat指定日期格式的字符串
Date parse(String source) String–>Date
将给定的字符串按照SimpleDateFormat指定日期格式解析为Date对象;
注: 如果字符串格式和SimpleDateFormat格式不一致,在解析时会发生异常: java.text.ParseException: Unparseable date
3. Calendar
- java.util.Calendar 抽象类,常用实现类是格里高里历(阳历)。
- getInstance() 使用Calendar静态方法创建实例。
- Date getTime() Calendar–>Date
- void setTime(Date date) Date–>Calendar
- void set(int field, int value) 针对不同时间分量单独设置值
- 设定月: 月从0开始,0表示1月,或者可以使用具体月的常量
- 设定日: DATE月中天、DAT_OF_MONTH与DATE一致
- int get(int field) 获取对应时间分量的值
- 获取月: 需要加1,因为月是从0开始
- 获取时: HOUR是12小时制、HOUR_OF_DAY是24小时制
- 获取星期: 国外是从星期日开始,从1开始
- int getActualMaximum(int field) 获取某一时间分量所允许的最大值
- void add(int field, int value) 对指定时间分量加上给定的值
若给定的值是负数,则是减去给定的值 - 日期开发思路
- Step1.获取用户输入的日期字符串
- Step2.使用SimpleDateFormat将其转换为Date
- Step3.创建一个Calendar,使其表示Date表示的日期
- Step4.使用Calendar根据需求计算时间
- Step5.将Calendar转换为一个Date
- Step6.使用SimpleDateFormat将Date转换为字符串后显示给用户