1.System.exit(int status):main方法正常退出时,程序的退出代码为0(通常都不写这句,默认的),表示成功运行了程序,若要返回其他退出代码,使用System.exit方法。System.exit方法的作用是中断虚拟机运行,使程序结束运行。return的作用是退出本方法,返回上一层(调用者)
2.三种注释。注释不会出现在可执行程序中,只是在源码中,是给人看的
a). //
b). /* */
c). /** */可以用来自动生成注释文档
3.强类型:一个变量在确定了其数据类型后,其数据类型就固定了不会变,除非进行强制类型转换操作
弱类型:数据类型区分没强类型那么严格,如String a=1,在运算操作时,a可以是字符串1,也可以是数字1,而不需要强制类型转换就可以直接参与运算。
4.动态语言:数据类型是在运行时确定的。
静态语言:数据类型是在编译时确定的。详细解释
5.数据类型:8种基本数据类型,byte(1),short(2),int(4,整形默认),long(8);float(4),double(8,浮点型默认,浮点数是不准确的,精度问题需要特别注意,2.0-1.1并不等于理想中的0.9);char(2或4);boolean(1或4)
boolean(1或4)的理由来源是《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。那虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗。大多数人都会很自然的这样去想,我同样也有这个疑问,经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。
6.特殊值:正无穷大,负无穷大,NaN(判断x是否为NaN,Double.isNaN(x))
int.class == Integer.TYPE//true
int.class == Integer.class//false
基本类型的包装类都有三个静态变量BYTES、SIZE、TYPE用来表示对应的基本类型存储所用的字节数,位数和class对象(Boolean只有TYPE,没有BYTES和SIZE,因为java规范没具体指明boolean需要占据几个字节,单独使用时占据4个,boolean数组则每个boolean元素占据1个)
Character类的charCount方法用来判断一个给定的代码点对应的字符是哪种类型的,如果大于等于0x10000则返回2,小于则返回1。对于传进来的代码点参数是否有效,该方法不进行验证,需要调用者自己调用isValidCodePoint方法自行验证
bitCount方法统计数值二进制形式中1的个数
unicode是字符集的名称,utf-8/16/32等是具体的实现方法
7.char字符集和编码方式:utf-16,代码点(代码值,类似于编号)和代码单元(2个字节大小,详见卷一P36-37),每个字符都有唯一的一个代码点(编号),用16位来表示一个代码单元,基本的字符只需一个代码单元,有的字符需要两个代码单元来表示,基本的字符并没有把65536(2的16次方)个编号都用完,还剩下2048个编号,这2048个编号分成两份A(a1~a1024)和B(b1~b1024),每份1024个编号,用两个编号如a1b1来表示基本字符之外的那些字符,这样有1024x1024种组合(如果采取排列的方法,应该可以有A(2048,2)即2048x2047种组合,可以表示更多字符),然后根据有关的算法,每种组合表示一个代码点。string.length()求的是代码单元数,string.codePointCount()求的是代码点数(实际的字符数)
8.类常量:static final修饰
for(;;)是一个死循环,相当于while(true)
9.运算符:&&和||为“短路”方式的逻辑与和逻辑或;三元运算符?:
当&的两个操作数不是boolean型变量时,则进行按位与运算,同理|也是
<<,>>(高位填充符号位),>>>(高位填充0)
数值在计算机中是以补码的形式存储的
一. 机器数和真值
在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念.
1、机器数
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。
那么,这里的 00000011 和 10000011 就是机器数。
2、真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
二. 原码, 反码, 补码的基础概念和计算方法.
在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.
1. 原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111]
即
[-127 , 127]
原码是人脑最容易理解和计算的表示方式.
2. 反码
反码的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.
3. 补码
补码的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.
三. 为何要使用原码, 反码和补码
在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.
现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
10.Math类:Math.sqrt(x),Math.pow(x,a),Math.round(),Math.PI,Math.E,Math.random()返回一个[0,1)之间的浮点数,更精确的还有StrictMath类
11.静态导入:import static java.lang.Math.*,也可以静态导入某一个特定的静态数据域或静态方法
12.数值类型转换:不同类型的数值进行运算,会将操作数转换成同一类型再进行计算,
转换的优先级为double>float>long>int
13.强制类型转换
14运算符优先级和结合性:先看优先级,优先级相同再看结合性,大多数为左结合,少数几个为右结合,如赋值,三元运算符,+=类和一元运算符。三元运算符前面的如果为真,后面的就不会执行了
int a = 1,b = 2,s = 0;
s = a>0?100:b++;
System.out.println(s);//100
System.out.println(b);//2
a+=b+=c相当于a+=(b+=c),
int a = 1,b = 2,c = 3;
a+=b+=c;
System.out.println(a);//6
a?b:c?d:e相当于a?b:(c?d:e)//先执行前面的
int a = 1,b = 2,s = 0;
s = a>0?b:++b>2?200:300;
System.out.println(s);//2
System.out.println(b);//2
15.枚举类型:举例如下
enum Size {S,M,L,XL}
Size s = Size.S;
16.字符串String。String类被final修饰,而且String对象所维护的值是不可变的,对于String s = "hello","hello"才是真正的对象,而且是不可变对象,s只不过是指向这个字符串常量的一个引用。
final修饰变量,如果是基本类型,则变量不能被从新赋值,而且值也不会变;如果是引用类型,则不能再引用(指向不能再改变)其他对象,但是指向的对象本身的内容可以变(不可变类除外)。
final修饰方法,可以被继承,但是不能被重写
final修饰的类,不能被继承,成员变量可以是final的,也可以不是,但成员方法自动为final
不可变类:对象一旦被创建完成,对象的成员变量的值将不能被改变
如果一个类要被声明为static的,只有一种情况,就是静态内部类
17.String类的API
subString(m,n):包前不包后
equals():注意与==的区别,==比较的是内存地址,即是否为同一个对象,String的equals方法已经重写过了,Object类的equals方法实际上就是==
equalsIgnoreCase()
compareTo()
endsWith(),startsWith()
toLowerCase(),toUpperCase()
length(),codePointCount()
trim(),replace(),format()
注意subString等方法和+产生的都是新的String对象
18.StringBuilder(StringBuffer效率比StringBuilder低,但是线程安全)append和toString方法
String根据字符串本身的内容重写了hashCode方法,StringBuilder和StringBuffer没有,值为内存地址(并非真的内存地址,而是在虚拟机中的内存地址)
19.标准输入Scanner in = new Scanner(System.in);
String name = in.nextLine();//next line
String color = in.next();//next word
int age = in.nextInt();//next int
上面的输入是可见的,如果读取密码的话,用下面的(只能在终端,不能使用IDE,否则会报空指针错误,因为IDE占用了console对象,console对象是单例对象)
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] password = cons.readPassword("Password: ");//密码使用数组存储,因为使用字符串不安全
字符串放在常量池,存在的时间长,且不能被修改,使用完后不能马上覆盖掉(直到gc回收),使用完后,需要将数组的值覆盖掉
20.局部变量初始化:不一定要在声明的时候立刻初始化,如
int a;
a = 2;这是完全可以的
21.大数值:BigInteger(大整数)和BigDecimal(大浮点数)
大数值运算不能使用+,-,*,/,%符号,而是使用对应的方法
add subtract multiply divide mod compareTo valueOf
22.数组
int[] a = {1,2,3,4,5};
int[] a = new int[] {1,2,3,4,5};
new int[] {1,2,3,4,5};//创建匿名数组
int[] a = new int[10];//数组长度可以是变量 int n = 10;int[] a = new int[n];
创建一个整数数组时,所有元素初始化为0;boolean数组元素都为false;对象数组元素都为null
23.一些接口
Formattable
Iterable
24.一些API
Arrays.toString(a)//将数组a转换成字符串(形如[1,2,3])
Arrays.deepToString(a)//将二维数组转换成字符串
Arrays.copyOf(a,length)//将a数组的每个元素拷贝到长度为length的新数组,
若length<a.length,则拷贝前面的;若length>a.length,则多余的用默认值填充
Arrays.sort(a)//使用快速排序法对数组进行排序
Arrays.binarySearch(a,b)//返回b在数组a中的索引值,若a中没有b,则返回一个负数r,-r-1是保持a有序b应该插入a中的位置
Arrays.equals(a,b)//比较两个数组是否相同,如果长度相同,且a[i]和b[i]相同,则返回true
Random:
nextInt(n):生成一个0~n-1的随机数
25.for each
for(type variable : collection) {...}//collection为数组或者实现了Iterable接口
for(int x : a) {System.out.println(x);}
26.main方法的参数String[] args的意义
27.类之间的关系
依赖(uses-a):类A的方法操作类B的对象,A只是单纯的new一个B的对象,然后调用对象的方法
聚合(has-a):类A的对象包含类B的对象,即A数据域是B类型的如兔子有腿
继承(is-a):类A继承于类B
28.有的类没有数据域,只有方法域(如Math)
29.除了构造方法和类同名,其他的方法也可以跟类的名字一样
30.对象变量和对象:一个对象变量并不包含一个对象,它只是对象的引用,new操作符的返回值就是一个引用
31.Date和GregorianCalendar,前者表示的是一个时间点(距离1970年1月1日00:00:00的毫秒数),后者是日历表示,是时间点的表示,同一个时间点表示形式有多种
GregorianCalendar月份从0开始计数
二者之间的转换Date time,GregorianCalendar calendar;time = calendar.getTime(); calendar.setTime(time)
32.隐式参数this:类的方法(静态方法除外)有个隐式(implicit含蓄的)this,指向调用该方法的对象本身
33.更改器方法的好处:封装性,而且可以在修改前进行错误检查
34.访问器方法:在编写访问器的时候,如果访问的字段是一个对象,那不应该返回该对象的引用(根据实际情况决定),因为返回对象的引用使得该对象可以被外面的程序修改,破坏了数据的私有性,而应该先对该对象进行克隆(深克隆),再返回克隆对象,当然像String类型的不可变对象除外,因为String对象不能被修改
35.一个方法可以访问该方法所属类的所有的对象的私有数据,如
class A {
private String name;
public boolean myEquals(A other) {
return name.equals(other.name);
}
}
当执行harry.equals(boss)的时候,equals方法不仅访问了harry的name,还访问了boss的name
36.final修饰数据域的时候,应该用于基本类型(primitive)或不可变类(如String,String之所以是不可变类,不是因为有关键词final修饰,而是String对象维护的值不可变,String对象维护着一个final修饰的char[]),对于可变的类会造成混乱。注意两种变:对象变量所指向的对象发生改变(堆中的对象本身改变),对象变量的指向发生改变(对象变量本身的值变了),指向了其他的对象(详见卷一p115)
37.静态域(static修饰又称类域),静态常量(static final修饰),静态方法(不能访问自身类的非静态属性和非静态方法,可以访问自身类的静态域和静态方法)
38.java函数传参总是按值调用,方法得到的是所有实参值的一个拷贝,并不能修改实参值(卷一p121)
39.构造器没有对类的数据域进行初始化的话(不良的习惯),数据域会有默认的初始化值,数值为0,布尔为false,对象引用为null,这是和局部变量的区别,局部变量必须明确的初始化
40.如果一个类没有编写构造函数,会自动提供一个无参数的构造函数,所有的数据域会被默认初始化;但是如果编写了构造函数,那就不再自动提供构造函数
41.构造函数的形参命名技巧:a.使用数据域一样的名字,方法内添加this区分;b.数据域的名字+前缀a,如数据域为name,形参就用aName
42.构造器中调用另一个构造器:在一个构造器中使用this(...)的形式调用另一个构造器(必须放在第一句)
43.数据域的初始化:构造函数中初始化,声明的时候初始化,初始代码块中初始化
44.初始化块(卷一p130-131):包在一对花括号中的代码{...},每次构造对象的时候都会执行一次
45.静态代码块(卷一p131):static{...},只在类第一次加载的时候执行(所以一个类没有main方法也能执行,记得在静态代码块最后加一句System.exit(0),否则报没定义main方法的错误信息)
46.执行顺序:
①静态代码块(类第一次加载执行,只执行一次)和静态数据域初始化——数据域默认初始化——按照声明的顺序或初始化块的顺序执行声明时的初始化(如果有的话)和初始化块——如果构造器中第一行调用了另一个构造器A,则执行构造器A,再执行本构造器
②父类静态代码块和静态数据域初始化——子类静态代码块和静态数据域初始化——父初始化(非静态)——父构造器——子初始化(非静态)——子构造器
47.finalize方法
48.如果在源文件头没有package语句,则这个类放在一个没有名字的默认的包中
49.如果没有指定public或private,那么可以被同一个包中的所有方法访问(包可见)
50.一些安全机制:JDK实现者禁止类加载器加载用户定义的包名以"java."开头的类。用户可以使用包密封机制,将一个包密封起来,就不能再向这个包添加类了
51.呵呵,国家法律为啥要绑定微信?