1、返回当前时间戳:Systrem.currentTimeMillis();
小于10的个位数 %10 等于它本身;
大于等于10的数%10等于它的个位数;
2、i++:先把 i 的值拿来用,然后再自增1;
++i:先自增1,再把自增1之后的新值拿来用;
3、科学计数法:System.out.println(1.2e2);
输出结果为:120,1.2e2=1.2 x 10^2;
4、整形直接量可以写为:12_0,等同于120,但 "_" 只能在数字之间,不能再首尾;
5、使用嵌套 if-else 时要注意,同一个块中的 else 总是和离他最近的 if 匹配;
6、由于取证错误的存在,浮点数之间的比较并不可靠,所以引入阈值 "ε" ,写作:EPSILON,例如:
final double EPSILON = 1e-14;
double a = 1.0-0.1-0.1-0.1-0.1-0.1;
if(Math.abs(A-0.5)<EPSILON){
System.out.println(“a等于0.5结果为真”);
}
7、可以在代码中适当的简化代码,以增强代码的可读性,同时也可以避免错误:
boolean even=false;
if(number%2==0){
even=true;
}else{
even=false;
}
//以上代码可以简写为:
boolean even=number%2==0;
8、终止程序也可以使用System.exit(),表示返回程序的最上层;
括号内的参数为0表示正常退出,非0表示非正常退出;
return 是结束当前方法,返回上一层;
break 是跳出当前整个循环;
continue 是结束本次循环,开始下一次循环;
9、操作符的优先级:算数 > 关系 > 逻辑 > 赋值;
10、Math类提供的常用数学函数:三角函数方法,指数函数方法,服务方法;
向上取整:Math.ceil();
向下取整:Math.floor();
返回最大值或最小值:Math.min / max(x,y);
返回绝对值:Math.abs();
返回 a 的 b 次方:Math.pow(a,b);
11、随机数公式
产生一个m〜n之间的随机数,不包含n:(int)(Math.random()*(n-m))+m;
12、char类型
(1)、自增与自减也可以用在char类型的变量上,这会使得这个字符变成之前或之后的Unicode的码;
(2)、char型数据可以转换为任意一种数值类型,反之亦然;
(3)、将一个浮点数转换为 char 字符时,先将浮点数转换为整型,然后再转换为字符类型;
(4)、当一个字符型数据转换为数值类型时,这个字符的Unicode的码就会被转换为特定的数值类型,如果转换结果适用于目标变量,就可以使用隐式转换,否则必须显式转换,例如:
double a ='b';
//输出结果为:98.0
(5)、所有的数值操作符都可以用在char上,如果另一个操作数是一个数字或者字符,那么字符类型就会被自动转换为一个数字,如果另一个操作数是一个字符串,那么字符就会与该字符串执行拼接;
(6)、两个字符可以使用关系操作符进行比较,这是通过比较两个字符的Unicode的值实现的;
boolean even='a'>'b';
System.out.println(even);
/**
*输出结果为:false
*a在Unicode表中的十进制表示为97
*b为98
*/
(7)、在Unicode码中,常用字符的编码:
'0'~'9':48〜57;
'A'~'Z':65〜90;
'a'~'z':97〜122;
(8)、在的Unicode码中,数字,小写字母,大写字母都是连续的整数,利用这个特性就可以检测某个字符属于哪种类型,例如:
char a ='f';
if(a> ='a'&& a <='z'){
System.out.println(“一个是小写字母”);
} else if(a> ='A'&& a <='Z'){
System.out.println(“一个是大写字母”);
} else if(a> ='0'&& a <='9'){
System.out.println(“一个是一个数字”);
}
(9)、char类中的常用方法:
方法 | 描述 |
isdigint(ch) | 如果指定的字符是一个数字,返冋true |
isLetter(ch) | 如果指定的字符是一个字母,返冋true |
isLetterOrDigit(ch) | 如果指定的字符是一个字母或者数字,返回true |
isLowerCase(ch) | 如果指定的字符是一个小写字母,返冋true |
isUpperCase(ch) | 如果指定的字符是一个大写字母,返冋true |
toLowerCase(ch) | 返回指定的字符的小写形式 |
toUpperCase(ch) | 返回指定的宇符的大写形式 |
(10)、Scanner类读取是明文的,安全性方面存在问题,所以jdk1.6之后加入了Console类来解决这个问题,但只能在cmd窗口下使用,在eclipse等IDE中无法使用,也就是说,这玩意儿没用:
13、特殊字符和转义字符
转义字符由反斜杠 "\" 后面加上一个字符或者一些数字组成,\t 代表Tab字符的转义符,\u03b1 表示一个Unicode码:
\ b:退格符;
\ t:Tab键;
\ n:换行符;
\ f:换页符;
\ r:回车符;
\ \:反斜杠;
\ “:双引号;
14、String类()
(1)、String类与System和Scanner类一样,都是java的库中的一个预定义类;
(2)、字符串类型不是基本类型,而是引用类型;
String a="welcome to java!";
//意思为:讲一个内容为welcome to java!的对象的引用,赋值给变量a;
(3)、字符串除了可以用 "+" 号进行拼接外,还可以使用String.join()方法进行拼接,例如:
String a=String.join("/","a","b","c","d");
//输出为:a/b/c/d,其中 "/" 为分隔符,也叫界定符
(4)、字符串对象的简单方法
方法 | 描述 |
length() | 返回字符串中的字符数 |
s1.charAt(index) | 返回字符串S1中指定位置的字符 |
s1.concat(S2) | 将S1和S2连接,返回一个新字符串(S1在前) |
toUpperCase() | 返回一个新字符串,其中所有的字母大写 |
toLowerCase() | 返回一个新字符串,其中所有的字母小写 |
trim() | 返回一个新字符串,去掉了两边的空白字符,\ t \ n \ f等都算空白字符 |
(5)、实例方法只能以特定的实例对象来调用;
静态方法可以使用类名或者对象名来调用;
实例方法中可以调用静态方法和静态数据域;
静态方法中不能直接调用实例方法和实例数据域,除非是实例对象名调用;
(6)、Math类是一个静态类,其中的方法都可以使用Math.xxx( )的方式来直接调用;
(7)、java允许在不创建新变量的情况下,使用字符串直接量调用字符串类中的方法,例如:
"welcome to java!".length();
//这是正确的,它的长度为16,包含空格
(8)、从控制台读取字符串时,input.next( )读取的是第一个空白字符之前的字符串内容,如果想读取一整行内容,可以使用input.nextLine()方法读取,它读取的是以回车键为结束标志之前的所有内容,包含空格,例如:
package test;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Please enter:");
System.out.println(input.next());
/**
* 输出:welcome
* 当输入 "welcome to java" 时,实际上只读取了第一个空白符之前的"welcome"
*/
}
}
-------------------------------------------------------------------------------------------
package test;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Please enter:");
System.out.println(input.nextLine());
/**
* 输出:welcome to java
*/
}
}
(9)、字符串对象的比较方法
方法 | 描述 |
s1.equals(S2) | S1和S2相等,返回真 |
s1.equalsIgnoreCase(S2) | 同上,不区分大小写比较 |
s1.compareTo(S2) | 返回一个大于0,等于0,小于0的整数,表示S1是否大于,等于或者小于S2 |
s1.compareToIgnoreCase(S2) | 同上,不区分大小写比较 |
s1.startsWith的(a) | S1是否以指定的字符串开头,返回真 |
s1.endsWith的(a) | S1是否以指定的字符串结尾,返回真 |
s1.contains(S2) | S2是否包含在S1中 |
(10)、当用 "==" 比较两个字符串的时候,比较的是引用,如果要比较内容,请用equals()方法;
(11)、"+" 和subString等操作产生的结果并不共享,而是独立存在与内存中的一个新字符串,除非截取字符串的时候是整个截取;
(12)、使用String.replace(CharSequence oldString,CharSequence newString)方法可以变相的改变字符串,例如:
String a="java";
//此时的java已经变为了html,等价于:a="html"
System.out.println(a.replace("java", "html"));
(13)、当用compareTo比较两个字符串的时候,在不等情况下返回的数字是取决于26个字母的顺序的,返回的是两个字符串第一个不同的字符的差值;
(14)、截取字符串的方法
方法 | 描述 |
subString(begin) | 从某处开始截取该字符串 |
subString(begin,end) | 从开始位置开始截取字符串,到端-1结束,不包含结束 |
(15)、获取字符串中的子串或者字符
方法 | 描述 |
s1.indexOf(ch) | 返回字符串中字一个出现的字符通道的下标,没有返回-1 |
s1.indexOf(ch,beigin) | 从开始位置开始返回第一个出现的字符CH的下标,没有返回-1 |
s1.indexOf(S2) | 返回字符串中第一个出现的字符串S2的下标,没有返回-1 |
s1.indexOf(S2,beigin) | 从开始位置开始返回第一个出现的字符串S2的下标,没有返回-1 |
s1.lastIndexOf(ch) | 返回字符串中出现的最后一个字符通道的下标,没有返回-1 |
s1.lastIndexOf(ch,begin) | 返回字符串中开始位置之前出现的最后一个字符通道的下标,没有返回-1 |
s1.lastIndexOf(S2) | 返回字符串中最后一个出现的字符串S2的下标,没有返回-1 |
s1.lastIndexOf(S2,begin) | 返回字符串中开始位置之前出现的最后一个字符串S2的下标,没有返回-1 |
(16)、要将字符串转换为int值,使用int a = Integer.parseInt(str),str必须是数值型的字符串,例如“123”,类似方法还有Double.parseDouble(str),也可以将数值转换为字符串,只需要简单的拼接一个空串就行;
(17)、格式化控制台输出
1、日期和时间的转换符
转换符 | 类型 | 举例 |
c | 完整的日期和时间 | 周二 8月 28 19:34:34 CST 2018 |
F | ISO8601日期 | 2015-02-09 |
D | 美国格式的日期(月/日/年) | 02/09/2015 |
T | 24小时时间 | 15:05:26 |
r | 12小时时间 | 06:05:19 pm |
R | 24小时没有秒 | |
Y | 4位数字的年,前补0 | 0786年 |
y | 年的后两位 | |
C | 年的前两位 | |
B | 月的完整拼写 | February |
b or h | 月的缩写 | Feb |
m | 两位数字的月,前补0 | |
d | 两位数字的日,前补0 | |
e | 同上不补0 | |
A | 星期的完整拼写 | |
a | 同上,缩写 | |
j | 三位数年中的某天,前补0,001~366 | |
H | 两位数字的小时,前补0,0~23 | |
k | 两位数字的小时,不补0,0~23 | |
I ( 大写 "i" ) | 两位数字的小时,前补0,0~12 | |
l ( 小写 "L" ) | 两位数字的小时,不补0,0~12 | |
M | 两位数字的分钟,前补0 | |
S | 两位数字的秒,前补0 | |
L | 三位数字的毫秒,前补0 | |
N | 九位数子的毫微妙,前补0 | |
p | 上午或下午的标志 | |
z | 从GMT起,RFC822数字位移 | |
Z | 时区 | |
s | 从格林威治时间1970-01-01 00:00:00起的秒数 | |
Q | 从格林威治时间1970-01-01 00:00:00起的毫秒数 |
package test;
import java.util.Date;
public class TestDemo {
public static void main(String[] args) {
/**
* 第一个参数是"时间:",第二个参数是 new Date()
* 第一行代码的意思是:索引为1的参数以字符串格式输出,
* 索引为2的参数输出的是时间和日期格式,先输出月的完
* 整拼写,最后输出4位的年
*/
System.out.printf("%1$s %2$tB %2$te , %2$tY","时间:",new Date());
/**
* 第二行代码的意思与第一行代码相同,'<' 的意思是:前
* 面格式说明中的参数将被再次引用
*/
System.out.printf("%s %tB %<te , %<tY","时间:",new Date());
}
}
System.out.printf("%tc",new Date());
//输出:周二 8月 28 19:34:34 CST 2018
标识符 | 输出 | 举例 |
%b | 布尔值 | trur或false |
%c | 字符 | 'c' |
%d | 十进制整数 | 200 |
%e | 标准科学计数法格式的数字 | 4.556 000e + 01 |
%f | 浮点数 | 45.560 000 |
%s | 字符串 | “Java”的(不含双引号) |
%o | 八进制数 | 144(十进制:100) |
%x | 十六进制数 | 64(十进制:100) |
%% | % | 输出一个% |
指定精度 :
%6b:false前加1个空格,true前加2个空格,因为false长度为5,true长度为4;
%6c:输出字符并在这个字符前面加上(6-1)个空格,必须满足n-1> = 0;
%6d:输出十进制整数,长度至少为6,如果该整数长度小于6,则在数字前面补齐剩余空格,大于6,则自动增加长度;
%10.2e:输出的科学计数法的数字整体长度至少为10,小数部分精度为2,例如:1.00e + 02,整体长度为8,所以要在数字前面补齐剩余的两个空格;
%10.2f:输出的浮点数整体长度至少为10,小数部分精度为2,例如:100.00,整体长度为6,所以在数字前面补齐剩余的四个空格;
%12s:输出的字符串长度至少为12,如果小于该长度,就在该字符串前补齐剩余空格,如果大于12,则自动增加宽度;
int a=5;
double b=45.56;
System.out.printf("a等于%-6d,b等于%6.1f",a,b);
/**
* 输出:a等于5[][][][][],b等于[][]45.6
* 其中:[]表示一个空格,浮点数为四舍五入
* 默认情况是左对齐的,可以在%后加一个'-',设置为左对齐,左对齐情况下空格追加在右侧
*/
注意:
1.条目与标识符必须在次序,数量,类型上匹配,默认情况下 %f 显示小数点后6位数字,可以在标识符中指定长度和精度;
2.如果一个条目需要比指定更长的长度,则长度自动增加;
3.格式是严格匹配的,格式标识符 %f 和 %e 必须对应浮点数,不能匹配int类型数值;
标志 | 目的 | 举例 |
+ | 打印正数和负数的符号 | +3333.33 |
空格 | 在正数之前添加空格 | |
0 | 数字前面补0 | 003333.33 |
- | 左对齐 | |
( | 将负数括在括号内,会省去负数的符号 | |
, | 添加分组分隔符,也就是从右向左,没三位一个逗号分隔,就是银行的金额表示法 | 3,333.33 |
$ | 给定被格式化的参数索引,例如%1$d,%1$x的意思是以十进制和十六进制打印第一个参数 | 159 6F |
< | 格式化前面说明的数值,%d%<x的意思是以十进制和十六进制打印同一个数值 |
package test;
public class TestDemo {
public static void main(String[] args) {
int a=55555;
int b=a-66666;
System.out.printf("正数:%1$d,%1$x,负数:%+(d",a,b);
//输出:正数:55555,d903,负数:+55555
}
}
15、循环
(1)、在循环中不能使用浮点数作为循环条件的判断依据,因为浮点数是某些值得近似值;
(2)、while循环和for循环都称为前测循环,do-while循环称为后测循环;
(3)、十进制数转换为十六进制数:143/16 = 8余15,十六进制数中的15是用F表示的,所以8F就是143的十六进制数;
(4)、continue是跳出本次循环,进行循环条件判断后,开始下一次循环
break是跳出当前整个循环;
(5)、在for循环中continue之后的操作是执行迭代操作,之后再进行条件判断,是否开始下一次循环;
while循环和do-while循环是在continue之后即刻进行条件判断,是否开始下一次循环;
16、方法
(1)、在某些编程语言中,方法称为过程或者函数;
有返回值的称为函数;
返回值类型为 void 称为过程;
(2)、调用方法也称为 invoke it 或 call it;
(3)、如果方法调用者对于返回值不感兴趣,那么也可以当做一般调用语句来处理;
(4)、当调用一个方法时,程序的控制权便转交给了被调用的方法,当被调用方法执行完毕之后,控制权由被调用方法返还给程序;
(5)、main 方法是由jvm调用的;
(6)、方法的执行过程
当开始执行主方法的时候,系统会在一片内存区域中,创建一个区域,称为活动记录或者活动框架,用于保存方法中的参数和变量,这个行为也称为调用堆栈、执行堆栈、运行时堆栈或者机器堆栈,通常我们简称为堆栈;
当在主方法中调用别的方法的时候,当前主方法的活动框架保持不变,一个新的活动框架被创建于被调用的方法,当被调用方法执行完毕之后,其相应的活动框架也被释放,最后主方法执行完毕时,主方法相应的活动框架也被释放;
(7)、方法名和参数列表一起构成方法的签名,不包括方法的返回值类型和访问修饰符;
(8)、向方法中传参和从方法中返回值
基本数据类型是按值传递,引用数据类型是传递引用,此处不再老生常谈;
17、return在java中的作用
1、return 语句作为一个无条件的分支,无需判断条件即刻发生;
2、有返回值的情况下返回该返回值,并结束该方法;
无返回值的情况下是结束当前方法,继续执行之后的代码;
package test;
public class Test {
public static int demo() {
int a=1;
return a;
}
public static void demo2() {
System.out.print(2);
return;
}
public static void main(String[] args) {
int a = Test.demo();
Test.demo2();
System.out.print(3);
}
}
/**
* 输出:23
*/
3、在try-catch-finally结构中
(1)、try块和catch块中都有 return 返回返回值;
之所以都有,是为了避免发生错误或者不发生错误中,任何一种情况发生,造成没有返回值的局面;
(2)、try-catch结构外有一个 return 返回返回值;
无论是否发生错误,都会执行返回语句,但明显第一种更高效,因为可以立刻看到异常的抛出情况;
(3)、只有finally块中有一个 return 返回返回值;
因为是否发生错误,finally 块都会被执行;
(4)、try-catch-finally结构外有一个 return 返回返回值;
无论是否发生错误,都会在执行完finally块中的代码后,执行返回语句;
18、数组
(1)、局部变量都必须在使用前进行声明和赋值;
(2)、可以利用ASCII码表中的字符的十进制编码来产生随机字符(详见文章最后的附录1);
(3)、数组与String一样,也是属于引用数据类型;
(4)、double[ ] arr=new double[10],我们可以说 arr 是一个数组,但严格来说,arr 是一个含有10个double型元素数组引用的变量;
(5)、创建数组时必须指定数组的长度,从而确定数组大小,数组创建之后大小不能再更改;
(6)、使用数组初始化语法的时候,不适用 "new" 关键字;
(7)、使用 "=" 赋值
对于基本数据类型来说,是按值传递;
对于引用数据类型来说,传递的是 含有该内容的对象在内存地址中的引用 ;
(8)、jvm垃圾回收机制
引用数据类型的数据在使用 "=" 进行赋值的时候,传递的是含有该内容的对象的引用,之后被赋值的对象和原对象的引用,都指向了内存地址中的同一个对象,而不是在内存地址中开辟一个新的存储空间,被赋值对象原来的引用将变为垃圾,被jvm回收;
(9)、复制数组的方法
1.使用循环逐一复制数组中的元素
2.使用系统中的静态方法arrayCopy
3.实现Cloneable接口,重写Object类中的clone方法,使用clone方法复制数组,详见博客:JavaSE—抽象类和接口;
(10)、没有显示的引用变量创建的数组称为匿名数组;
同样的,还有匿名对象;
(11)、可变长参数列表
public static void getNum(int ... num){
//方法体
}
类似于上述这种可变长的参数列表,会被当做数组来处理,传参的时候可以传入一个数组,如果方法中还有其他常规参数,那么必须放在可变长参数列表的前面,例如:
public static void getNum(String str,int ... num){
//方法体
}
(12)、数组的查找
1.线性查找法
数组中的线性查找就是将要查找的内容与数组中的每一个元素相比较,直到找到相同内容或者全部比对完毕为止,找到了会返回相同内容在数组中的下标,如果没找到会返回-1,该查找方法所需的时间随着数组元素的增加而增加,所以对于大型数组来说,该方法的效率并不高;
2.二分查找法
与线性查找法相对的就是二分查找法,是用该方法的前提是数组已经按照顺序排列好,将要查找的内容与数组的中间内容进行比较,假如数组按照升序排列,考虑下面三种情况:
1.要查找内容大于数组中间元素,那么只需要在数组的前一半元素中进行查找;
2.要查找内容等于数组的中间元素,那么查找完毕;
3.要查找内容小于数组的中间内容,那么只需要在数组的后一半元素中进行查找;
二分查找法可一重复进行,一半一半的排除,不断的缩小范围,直到找到需要的内容;
(13)、java.util.Arrays中类中的操作数组的方法
1.排序:Arrays.sort(arr)和Arrays.parallelSort(arr),这两个方法还有重载方法,可以实现对数组中的一部分进行排序,Arrays.sort(arr,index1,index2)和Arrays.parallelSort(arr,index1,index2),可以对arr [index1] ~arr [index2-1]的元素进行排序;
2.查找:Arrays.binarySearch(arr,'a'),可以查找arr数组中是否存在'a',如果不存在,返回 - (插入点下标+1),如果存在,返回该元素下标,注意:此法为二分查找法,因此使用前必须用某种方法对数组进行排序;
3.另外还可以使用Arrays.equals(arr1,arr2)来比较两个数组是否相等,该方法为严格比较,只有元素和对应元素的顺序都相同时才返回真;
4. Arrays.fill(arr,1)将1填充到数组a中,也就是将整个数组的所有元素的值全部换为1;
Arrays.fill(arr,1,5,8)将8填充到数组一个的下标为1到5-1中;
(14)、锯齿数组
创建二维数组是必须至少指定第一维的长度,也就是行数,之后可以用arr [i] = new int [length]的方式指定每一行的长度,如果各行的长度不同,那么这个二维数组就称为锯齿数组;
19、对象和类
(1)、面向对象程序设计简称OOP;
(2)、类的模板和对象的图示可以使用统一建模语言的图形符号进行标准化,这种表示方法称为UML类图,简称类图;
(3)、可以把多个类放在同一个文件中,但文件中只能有一个类是public类,public类必须与文件同名,运行时系统会调用公共类的主方法,在编译时,每个类都会形成一个.class文件;
(4)、严格的讲,对象的引用变量和对象是不同的,但这种差异是可以忽略的,我们可以简单的说,Dog是一个Animal对象,而不用冗长的说,Dog是一个包含对Animal对象引用的变量;
(5)、对象的属性称为数据域;
(6)、类中的方法称为实例方法,因为只能用具体的实例来调用它,调用对象上的实例方法的过程称为调用对象;
(7)、有时候我们也可以在创建对象之后不引用它,例如:new Animal(),这种方式创建的对象称为匿名对象;
(8)、引用类型的数据域的默认值是null,例如:String,同样,null也是它的直接量;
(9)、基本类型变量在内存中存储的一个基本类型的值,而引用类型变量存储的是一个引用,它指向对象在内存地址中的位置;
(10)、创建一个Random对象时,必须指定一个种子,否则,使用系统已经逝去的时间为种子,如果两个Random对象拥有同样的种子,那么他们将会产生同样的随机数列,例如:
Random rand1 =new Random(1);
Random rand2 =new Random(1);
//他们产生的随机数列是一样的
(11)、Java API在javafx.geometry包中有一个便于使用的Point2D类,用于表示二维平面上的点,使用方法参照下图,示例用的是java下的Point2D类,javafx包下的Point2D类不是抽象类,是可以实例化的:
(12)、静态变量也称为类变量,被类中所有对象所共享,静态方法中不能访问类中的实例成员;
(13)、实例方法可以调用实例方法和静态方法,以及访问实例数据域和静态数据域;
静态方法只能访问静态数据域和静态方法,因为静态数据域和静态方法不属于某个特定的对象,但可以在静态方法中使用类的实例对象访问实例方法和实例数据域;
(14)、如何判断一个变量或方法应该是实例的还是静态的呢?
如果一个变量或者方法依赖于某个实例对象,那么就应该将它定义为是一个实例变量或方法,相反,就应该定义为一个静态变量或者方法;
(15)、private修饰符将访问权限限定在它自己的类中,默认修饰符将访问权限限定在包内,protected可以在当前包中和被其子类访问,而public修饰符可以无限制访问;
(16)、为避免对数据域的直接修改,使用private将数据域声明为私有的,这称为数据域封装;
针对私有数据域提供的获方法称为访问器(getter),而设方法称为修改器(setter);
(17)、数组也可以用来存储对象,对象数组实际上就是引用变量数组;
(18)、如果一个类是不可变类,那么它的所有数据域必定都是私有的,并且不存在修改器方法;
但如果一个类的数据域都是私有的并且没有修改器方法,它不一定是不可变类;
(19)、不可变类必须满足以下几个条件:
1.所有数据域都是私有的;
2.没有修改器方法;
3.没有一个返回指向可变数据域的引用的方法;
(20)、关键字this引用对象自身,也可以在构造方法内调用同一个类的其他构造方法;
静态方法中不能使用this引用数据域;
在一个构造方法中使用this调用另一个重载的构造方式时,该调用语句应该位于第一行;
(21)、类的关系通常是关联,聚合,组合以及继承;
(22)、栈是以一种“后进先出”的方式存放数据的数据结构;
(23)、基本数据类型可以包装成对象,俗称包装类,Boolean, Character, String, Double, Float, Byte, Short, Integer, Long这些包装类都在java.lang包下;
(24)、对于数值类型的包装类来说,既可以用基本数据类型值,也可以用表示数值的字符串来构成包装类,例如:new Double(5)和new Double(“5”)是一样的;
(25)、包装类都没有无参构造方法,这也就是说,一旦创建对象后,他们的内部值就不能再改变;
(26)、每一个数值包装类都有MAX_VALUE和MIN_VALUE,分别表示该包装类对应的基本数据类型的最大值和最小值;
(27)、对于浮点数来说,最小值指的是它们的最小正值;
(28)、对于每个数值包装类来说,都包含方法xxxValue(),XXX表示该包装类对应的基本数据类型,这些方法返回对应的包装类的基本数据类型的值;
(29)、数值包装类型还有一个静态方法,valueOf(String s),该方法创建一个新对象,并初始化为指定字符串表示的值,例如:
double a = Double.valueOf(“12.4”);
(30)、数值包装类都有一个用于比较两个数值大小的方法,compareTo()方法,如果该数值大于、等于或者小于另外一个数时,分别返回1、0、-1,例如:
new Double(12.4).compareTo(new Double(12.3));
//返回1
(31)、数值包装类都有一个方法,parseXxx(String s),Xxx表示对应的包装类的首字母大写的基本数值类型,可以将一个字符串类型的数值转换为包装类对应的基本数据类型的值,该包装类有一个重载方法,可以将指定进制的一个数转换为对应的十进制数据类型的数值,例如:
Integer.parseInt(“11”,2);
//将2进制的数11转换为对应数据类型的十进制数值,返回3
//另外还可以用String类下的format方法,将十进制数转换为八进制和十六进制,例如:
//将111转换为八进制
String.format(“%o”,111);
//将111转换为十六进制
String.format(“%x”,111);
(32)、十进制数转换为任意进制数(详见附录二);
(33)、BigInteger 和 BigDecimal 类
1.位于java.math包中,可以表示任意大小和精度的整数和浮点数;
2.BigDecimal对象的精度是没有限制的,但是结果不能终止,会抛出ArithmeticException异常,所以可以使用重载的divide方法来指定小数位精度和舍入方式,语法为:divide(BigDecimal d,int scale,BigDicemal .roundingMode ),其中scale指小数点后小数位的精度,roundingMode指舍入方式;
共有以下八种舍入方式:
1.ROUND_UP:远离零的舍入方式,例如:1.x ≈ 2,-1.x ≈ -2;
2.ROUND_DOWN:接近零的舍入方式,例如:1.x ≈ 1,-1.x ≈ -1;
3.ROUND_CEILING:接近正无穷大的舍入方式,例如:1.x ≈ 2,-1.x ≈ -1;
4.ROUND_FLOOR:接近负无穷大的舍入方式,例如:1.x ≈ 1,-1.x ≈ -2;
5.ROUND_HALF_UP:传说中的“四舍五入”,不多赘述;
6.ROUND_HALF_DOWN:楼上的大哥,“五舍六入”;
7.ROUND_HALF_EVEN:四舍六入,五分两种情况,如果舍弃部分的前一位为奇数,则入位,否则舍弃,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小,俗称“银行家舍入法”,主要在美国使用;
8.ROUND_UNNECESSARY:断言请求的操作具有精确结果,因此不需要舍入,此模式会抛出ArithmeticException异常;
BigDecimal a = new BigDecimal(1.0);
BigDecimal b = new BigDecimal(3);
BigDecimal c = a.divide(b,20,BigDecimal.ROUND_UP);
System.out.print(c);
//得出结果为:0.33333333333333333334;
20、String类
(1)、用字符数组构成字符串
(2)、字符串类包含静态format方法,可以创建一个格式化的字符串,类似于printf的方法,不同的是printf的方法是显示一个格式化的字符串,format方法是创建一个格式化的字符串,例如:
String s = String.format(“%5.2f%6d%-6s”,4.1,6,“AB”);
//s结果为:x4.10xxxxx6ABxxxx,一个x代表一个空格;
(3)、字符串变量存储的是对字符串对象的引用,字符串对象里存储的才是字符串的值;
(4)、字符串对象是不可变的,重新赋值之后的字符串对象改变的只不过是引用而已,例如:
String s =“java”,s =“html”,变量s的引用现在指向了新的String对象“html”,原来的String对象“java”还存在,只不过现在不能再使用它,所以jvm为了提高效率并节约内存,对具有相同字符序列的字符串直接量使用同一个实例,这样的实例称为限定字符串;
String a =“java”;
String b = new String(“java”);
String c =“java”;
//a == c,为true; a == b,为false; 因为a和c的引用指向了同一个字符串对象,b是在内存中重新创建的;
(5)、字符串对象的替换和提取
(6)、字符串类的replace和match方法非常相似,但match方法更加强大,不仅可以匹配定长的字符串,还可以匹配正则表达式;
(7)、代码点:Unicode码中字符的十进制表示数字;
代码单元:单个字符为一个代码单元;
(8)、StringBuilder和StringBuffer类似于String类,区别在于String类是不可改变的,但前两者更为灵活,可以在其中添加,插入或追加新的内容;
(9)、StringBuilder和StringBuffer中的构造方法和其他方法几乎是完全一样的;
(10)、如果是多任务并发访问,就使用StringBuffer,因为这种情况下需要同步,防止StringBuffer崩溃,如果是单任务访问,那么使用的StringBuilder会更加高效;
(11)、StringBuilder的共有三个构造方法和30多个用于管理构建器和修改构建器内字符串的方法,三个构造方法分别为:
new StringBuilder():构建一个容量为16的空字符串构建器;
new StringBuilder(int a):构建一个指定容量的字符串构建器;
new StringBuilder(String s):构建一个指定字符串的字符串构建器;
(12)、StingBuilder类提供了几个附加重载方法,可以将boolean,char,char [],double,float,int,long,String类型值追加到字符串构造器,例如:
(13)、
(14)、StringBuilder类还有以下一些方法:
方法 | 说明 |
toString() | 从字符串构建器返回一个字符串对象 |
capacity() | 返回该字符串构建器的容量,默认为16 |
charAt(int index) | 返回指定下标处的字符 |
length() | 返回该构建器中的字符数 |
setLength(int newLength) | 设置该构建器新的长度,如果长度小于已存储字符串的长度,则超过部分被截断 |
subString(int beginIndex) | 返回从beginIndex开始的子串 |
subString(int beginIndex,int endIndex) | 返回从beginIndex到endIndex-1的子串 |
appendCodePoint(int cp) | 追加一个代码点,并将其转换为一个或两个代码单元返回 |
trimToSize() | 减少用于字符串构建器的存储大小,如果构建器存储的字符数<16时,调用此方法将去掉构建器内除已存储字符串的长度,此时的构建器内容容量为已存储字符串的长度,如果构建器存储的字符数> = 16时,此方法将不再生效 |
(15)、实际上,字符串的长度总是小于或等于构建器的容量,长度是存储在构建器中字符串的实际大小,容量是构建器的当前大小;
(16)、构建器的容量如果不够用了,会自动追加;
(17)、在计算机内部,字符串构造器是一个字符数组,因此构建器的容量就是数组的大小,当构建器的容量不足时,构建器进行第一次长度追加的时候,会用一个新的数组替换掉原来旧的数组,新数组的长度为2 *(原数组长度+1),第二次或以上再次追加数组长度时,新数组的长度等于旧数组长度+新追加字符串长度;
(18)、如果一个字符串不需要改变,那么就使用字符串类就可以,反之,选择的StringBuilder类;
(19)、如果构建器容量大于已存储字符串长度的时候,可以调用trimToSize方法,将构建器容量降到实际需要的大小,以免造成不必要的内存浪费;
21、继承
(1)、如果类a拓展自类b,就将类a称为次类,将类b称为超类;
次类又称为子类,拓展类,派生类;
超类又称为父类,基类;
(2)、次类从超类中继承可访问的数据域和方法,还可以添加新的数据域和方法;
可以继承public和protected修饰的属性和方法,无论子类和父类是否在同一个包里;
如果想要继承默认权限修饰符的属性和方法,那么子类和父类必须在同一个包中;
无法继承private修饰的属性的方法;
无法继承父类的构造方法;
(3)、继承的关键字extends;
(4)、关于继承应该注意的几个点:
1.子类并不是父类的一个子集,实际上,一个子类通常比他的父类包含更多的信息和方法;
2.父类的私有数据域在该类之外是不允许访问的,因此不能在子类中直接使用,但是可以通过父类中定义的公共的访问器和修改器来访问和修改;
3.继承是用来为“is a”(是一个)关系建模的,并不是所有的“is a”(是一个)关系都应该用继承来建模,例如:从human类中派生tree类虽然都有可以共用的height属性,但这样派生是明显不合理的;
4.java中只允许单继承,如果想实现多继承,可以使用接口;
(5)、super关键字指代父类,可以调用父类中的普通方法和构造方法(私有的属性和方法除外);
(6)、继承关系中的构造方法:
1.如果子类的构造方法中没有通过super显示的调用父类的有参构造方法,也没有通过this显示调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法;
2.如果子类的构造方法中通过super显示的调用了父类的有参构造方法,则将执行父类相应的构造方法,而不会执行父类的无参构造方法;
3.如果子类的构造方法中通过this显示的调用了自身的其他构造方法,则在相应的构造方法中应用以上两条规则;
4.如果存在多级继承关系,则在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类对象类的无参构造方法为止;
(7)、this和super的应用规则
1.在构造方法中如果有this和super语句出现,则只能是第一条语句;
2.在一个构造方法中不允许同时出现this和super语句,否则就和第一条规则矛盾;
3.在类方法中不允许出现this和super语句;
4.实例方法中可以同时出现this和super语句,并且不要求是第一条语句;
(8)、父类的构造方法不会被子类继承,只能使用关键字super从子类的构造方法中去调用,并且调用语句必须出现在第一行,这是显式调用父类构造方法的唯一方式;
(9)、当构造一个子类的对象时,子类构造方法会在完成自己的任务之前,首先调用它的父类的构造方法,如果父类继承自其他类,那么父类又会在完成自己的任务之前,调用它自己父类的构造方法,这个过程一直持续到沿着这个继承体系的最后一个构造方法被调用为止,这就是构造方法链;
(10)、关于子类重写父类方法必须满足以下几个条件:
1.子类和父类的签名必须一致(方法名和参数列表必须一致);
2.子类的返回值必须和父类一致或者是其子类;
3.子类重写方法不能缩小父类方法的访问权限;
4.如果子类定义的方法在父类中是私有的,那么这不构成方法重写,这两个方法是没有任何关系的;
5.静态方法也是可以被继承的,如果父类中的静态方法在子类中被重新定义,那么在父类中定义的静态方法将会被隐藏,可以使用语法:父类名.父类静态方法名调用被隐藏的静态方法;
(11)、重载和重写的区别
1.重载意味着使用同样的方法名但是不同的签名来定义多个方法,与返回值和访问修饰符无关;
2.重写意味着方法在父类中定义,而在子类中提供一个新的方法实现,重写只能发生在通过继承而相关的不同类中;
(12)、如果在子类中想要调用被重写的父类方法,可以通过super.方法名来实现;
22、java.lang.Object继承类
(1)、如果在定义一个类的时候没有指定继承性,那么这个类的父类就被默认是Object;
(2)、Object类中经常被子类重写的方法:
toString():返回一个描述该对象的字符串;
equals():比较两个对象是否是同一个对象,若是,返回真;
clone():生成当前对象的一个副本,并返回;
hashCode():返回该对象的哈希代码值;
getClass():返回当前对象所属的类信息,返回类对象;
(3)、instanceof操作符用于判断一个对象是否属于一个类或者实现了一个接口;
语法:dog instanceof Dog;
注意:使用instanceof操作符时,对象的类型必须和第二个指定参数所指定的类或者接口在继承树上存在上下级关系,否则会出现编译错误;
(5)、重写a.equals(b)方法思路:
1.判断一个和b的引用是否一致,如一致返回真;
if(this== b){
return true ;
}
2.判断b是否属于一个的类,如不是返回假;
if(!(b instanceof a的类)){
return false;
}
3.将b强制转换为a的类,之后判断a和b的数据域是否一致,如一致,返回真,否则返回假;
a的类 obj =(a的类)b;
if(this.name.equals(obj.name)&& this.sex.equals(obj.sex)){
return true;
} else{
return false;
}
22、final修饰符
(1)、用final修饰的类,不能再被继承;
(2)、用final修饰的方法不能被子类重写;
(3)、被final修饰的变量将变成常量,只能被赋值一次
(4)、用final修饰引用型变量的时候,对象的引用不能改变,但对象的属性值是可以改变的;
23、多态
(1)、使用多态的原则:
1.父类声明引用变量指向子类,此时调用对象方法,调用的是对象重写的继承自父类的方法,无法调用子类特有方法,如需调用子类特有方法,需要将对象进行向下转型(强制类型转换);
2.可以将父类对象作为返回值实现多态(例如宠物领养例子);
(2)、面向对象程序设计的三大支柱是封装,继承和多态;
(3)、多态就是父类型的引用可以指向子类;
Pet pet=new Dog();
(4)、一个变量必须被声明为某种类型,这个类型称为它的声明类型;
引用类型的变量可以是一个空值或者是对声明类型实例的引用,而这个实例可以使用声明类型或它的子类型的构造方法创建,变量的实际类型是被变量引用的对象的实际类,例如:
Pet pet=new Dog();
pet.toString();
左边的Pet是对象pet的声明类型,右边的Dog是Pet的引用指向的实际类型,pet调用父类还是子类中的的toString方法,是由右边的实际类型的Dog决定的,也就是说,这里调用的的toString方法是子类Dog中的的toString方法,而不是父类Pet中的的toString方法,称为这动态绑定;
动态绑定工作机制如下:假设对象ö是类C1,C2,...,CN-1,CN的实例,其中C 1是C 2的子类,C2是C3的子类,...,CN-1是的C n的子类,也就是说,CN是最通用的类,C1是最特殊的类,在java的中,CN就相当于对象类,如果对象ö调用一个方法p,JVM会依次在类C1,C2,..,Cn-1,Cn中查找p的实现,直到找到为止,一旦找到一个实现就会停止查找,然后调用这个首先找到的实现;
(5)、Object a =new Student();
这称为隐式转换;
Student b = a;
此时会发生编译错误,因为学生类的实例对象,同时也是一个Object 类的实例对象,但Object 类的实例对象,却不一定是Student类的实例对象,所以,这里就必须使用显示转换或者强制转换,将实例一个转换为一个学生对象,之后才能将一个的引用赋值给学生对象b;
(6)、总是可以将一个子类的实例转换为一个父类的实例,称为向上转换,因为子类的实例永远是父类的实例;
例如:Pet pet=new Dog();
当把一个父类的实例转换为它的子类的实例时,这称为向下转型,必须显式的使用强制转换;
例如:Dog dog=(Dog)pet;
为确保转换成功,必须确保要转换的对象是子类的一个实例,否则就会发生运行错误;
一个好的方法是,在尝试转换之前确保对象是另一个对象的实例,可以使用的instanceof运算符来判断;
(7)、为什么要进行类型转换呢?
因为指向子类实例的父类的引用变量,无法调用子类中特有的方法,所以此时必须将父类的引用变量转换为子类类型,才可以调用子类中特有的方法,例如:
Pet pet=new Dog();
((Dog)pet).getMes();
getMes方法并不是继承自父类的,而是子类特有的方法;
如果getMes方法是子类继承自父类并且重写的方法,那么此时就不需要强制类型转换,但此时pet调用的是子类重写的方法;
(8)、鉴于多态的特性,在实际开发中,将变量定义为父类型,这样它就可以接受任何子类型的值;
(9)、另外还要注意区分基本类型值和对象的转换,例如:
age= 65 ;
byte newAge =(byte)age;
上面的基本类型数值的转换,是返回一个新值;
Pet pet=new Dog();
Dog dog=(Dog)pet;
现在pet和dog的引用指向同一个对象;
24、异常
(1)、处理异常的两种方式:
1.try-catch在当前位置捕获并处理异常;
2.声明抛出异常,交给上一级调用方法处理;
(2)、try-catch语句块:
1.如果所有语句全部正常执行完毕,不会发生异常,那么,catch块中的所有语句都会被忽略;
2.如果try语句块在执行中遇到异常,并且这个异常与catch中声明的异常相匹配,那么在try块中未执行的语句和其他不匹配的catch块将被忽略,而相应匹配的catch块将被执行;
3.如果try块在执行的过程中遇到异常,但该异常并未在catch块中声明,那么程序会立即退出;
(3)、在catch块中也可以加入用户自定义的处理信息,System.err.println(“程序出现错误!”);
(4)、catch 块中可以用void printStackTrace()方法输出异常的堆栈信息;
堆栈信息包括程序运行到当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用序列,throwFor是异常的抛出处,程序的主方法在最外层的方法调用处;
异常 | 说明 |
Exception | 异常层次结构的根类 |
ArithmeticException | 算术错误情形,如以零作除数 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问空对象成员 |
ClassNotFoundException | 不能加载所需的类 |
InputMismatchException | 欲得到的数据类型与实际输入的数据类型不匹配 |
IllegalArgumentException | 方法接收到非法参数 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把“ABC”转换为数字 |
(5)、其他常见异常参考: https ://www.cnblogs.com/cvst/p/5822373.html;
(6)、try-catch-finally程序块
无论是否发生异常,无论该异常是否在抓catch块中被声明,无论try块中是否存在return语句,finally块中的内容都会被执行;
发生异常时的执行顺序,执行try块或catch块中return之前的语句,执行finally块中的语句,执行try块或者catch块中的语句退出;
(7)、只有一种情况finally块不会被执行,那就是当try块或catch块中存在System.exit()的方法时,该方法将会推出jvm;
(8)、throw和throws
使用情形:
1.public static void main(String [] args)throws Exception{};
2.throw new Exception(“输入错误!”);
两者区别:
1.throw用于在程序中抛出异常;
throws用于声明,在该方法内抛出了异常;
2.throw用于方法体内部,可以作为单独语句使用;
throws必须跟在方法参数列表后,不能单独使用;
3.throw是用于抛出一个异常对象,且只能是一个;
throws后面可以跟异常类,而且可以跟多个异常类;
(9)、异常的分类
1.Throwable类:所有异常类型都是其子类,其派生出两个子类,即Error和Exception;
2.Error类:表示紧靠程序本身无法恢复的严重错误,该类错误不能捕获,如内存溢出,虚拟机错误等错误,发生这种错误的话除了只能尽力使程序安全退出外,其他的我们无能为力;
3.Exception:由java的应用程序抛出和处理的非严重错误 ;
4.运行时异常:包括的RuntimeException及其所有子类,不要求程序必须对它们进行处理,不影响编译和运行,如果在运行中遇到异常,会输出异常的堆栈信息并终止程序运行;
5.checked异常(非运行时异常):除了运行是异常外的其他由异常继承来的异常类,程序必须捕获或者声明抛出这种异常,否则会出现编译错误,无法通过编译,例如:的SQLException ,ClassNotFoundException;
附录1
1、ASCII、Unicode和UTF-8编码的区别
最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这就是ASCII码表;
后来随着计算机的发展和普及,各国相继颁布和制定了本国文字的计算机编码表,由于各国标准不同,所以就造成了在多语言混合文本中,不可避免的出现了冲突和乱码,因此,Unicode将所有语言都统一到一套编码中,这样就不会出现乱码了;
但新的问题有出现了,Unicode码表标准颁布之后,虽然消除了乱码,但对于英文传输来说,用Unicode码表比ASCII码表要多用一倍的空间,在存储和传输上成本非常高;
因此,又出现了把Unicode码表转化为 "可变长编码" 的UTF-8编码;
UTF-8编码根据不同的语言,将编码定位1-6个字节,常用的英文字母被编为1个字节,汉字通常是3个字节,是由很生僻的字符才会被编码为4-6个字节,这样在传输大量英文字符的文本时,UTF-8编码就能大幅度降低传输成本;
另外,UTF-8编码有一个额外的好处就是,ASCII编码实际上可以看成是UTF-8编码的一部分,因此,早起的很多只支持ASCII编码的历史遗留软件,可以在UTF-8编码的支持下继续工作;
附录2