数据类型
byte(1字节), short(2字节), int(4字节), long(8字节), float(4字节), double(8字节), char(2字节), boolean(true, false)
Java 没有任何无符号(unsigned) 形式的 int、long、short 或 byte 类型。
char 类型描述了 UTF-16 编码中的一个代码单元,占2字节,(有的字符编码占用两个代码单元)建议不要在程序中使用 char 类型。
整数被 0 除将会产生一个异常, 而浮点数被 0 除将会得到无穷大或 NaN 结果。
&&和||运算符是按照“ 短路”方式来求值的: 如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。
>>运算符会用符号位填充高位,>>>运算符会用 0 填充高位。
移位运算符的右操作数要完成模32的运算(除非左操作数是long类型,在这种情况下需要对右操作数模 64 )。 例如,1<<35 的值等同于 1<<3 。
1 | System.out.println ( 1.0 / 0 ); |
字符串“xxoo”是unicode字符x、x、o、o组成的序列。字符串、String类对象是不可变对象。
判断字符串内容相等用equals,==运算符是判断字符串的地址是否相等。
String取length是取的字符串的代码单元的个数。
1 | String hehe = "😄"; |
Console类用于输入密码,为了安全起见,返回的密码存放在一维字符数组中, 而不是字符串中。
1 | 因为字符串有字符串常量池,导致密码长期保存在内存中,容易通过jmap+jhat分析出密码来,而使用字符数组,用完即可置为null,相对安全 |
在 C++ 中, 可以在嵌套的块中重定义一个变量。 在内层定义的变量会覆盖在外层定义的变量。 但是,在java中不允许嵌套块中重定义外层的变量。
在 Java 中, 允许数组长度为 0。 在 Java 中, 允许将一个数组变量拷贝给 另一个数组变量。这时, 两个变量将引用同 一个数组。如果希望将 一个数组的所有值拷贝到一个新的数组中去, 就要使用 Arrays 类的 copyOf 方法。可以通过二维数组创建不规则数组(先创建行,再单独创建每行的数组)
1 | //浮点数默认为双精度的(double) |
类和对象
识别类的简单规则是在分析问题的过程中寻找名词, 而方法对应着动词。
关系 | 含义 |
---|---|
继承(is a) | is a,类A扩展自类B |
接口实现 | 类实现接口中的方法 |
依赖(use a) | use a,一个类的方法操纵另一个类的对象 |
聚合(has a) | has a,类A的对象包含类B的对象(属性关联) |
关联 | 通过属性、方法关联 |
构造器总是伴随着 new 操作符的执行被调用。
不要编写返回引用可变数据域对象的访问器方法。 如果需要返回一个可变数据域的拷贝, 就应该使用 clone。
- Get不使用clone,直接返回引用
- Get返回引用对象的clone
对于可变的类, 使用 final 修饰符可能会对读者造成混乱。
1 | class Test { |
静态变量使用得比较少, 但静态常量(static final)却使用得比较多。 静态方法是没有this参数的方法,在一个非静态方法中this参数是该方法的隐式参数,代表操作此方法的对象。
java方法调用参数是按值传递,下面总结一下 Java 中方法参数的使用情况:
• 一个方法不能修改一个基本数据类型的参数 (即数值型或布尔型)。
• 一个方法可以改变一个对象参数的状态。
• 一个方法不能让对象参数引用一个新的对象。
类属性与局部变量的主要不同点: 必须明确地初始化方法中的局部变量。 但是,如果没有初始化类中的属性, 它将会被自动初始化为默认值(0、false 或 null ),但是,这并不是一种良好的编程习惯。
仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器 。
可以在类定义中, 直接将一个值赋给任何属性。在执行构造器之前,将会先执行赋值操作。
类构造初始化次序:
- static 属性赋值
- static 初始化块
- 实例属性赋值
- 实例属性初始化块
- 构造函数体
可以为任何一个类添加 finalize 方法。finalize 方法将在垃圾回收器清除对象之前调用。 在实际应用中, 不要依赖于使用 finalize 方法回收任何短缺的资源,这是因为很难知道这个方法什么时候才能够调用。
从编译器的角度来看,嵌套的包之间没有任何关系。 例如, java.util 包与 java.util.jar 包毫无关系。每一个都拥有独立的类集合。
将包中的文件放到与完整的包名匹配的子目录中。
利用 -classpath 选项设置类路径是首选的方法, 也可以通过设置 CLASSPATH 环境变量 完成这个操作。
1 | java -classpath c:\classdir;.;c:\archives\archive.jar MyProg |
继承
如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认(没有参数 ) 的构造器。 如果超类没有不带参数的构造器,并且在子类的构造器中又没有显式地调用超类的其他构造器, 则 Java 编译器将报告错误。
是否应该设计为继承关系的一个简单规则: “is-a” 规则。它的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。
1 | Employee e; |
在 Java 中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。但是一定要注意这种用法会引起以下类型紊乱的错误。
1 | Manager[] managers = new Manager[10]; |
返回类型不是签名的一部分, 因此,在覆盖方法时,一定要保证返回类型的兼容性。允许子类将覆盖方法的返回类型定义为原返回类型的子类型。
方法调用的两种方式:静态绑定(private方法、static方法、final方法),动态绑定(其他)
如果将一个类声明为 final, 只有其中的方法自动地成为 final, 而不包括属性。
只能在继承层次内进行类型转换。 在将超类转换成子类之前,应该使用 instanceof 进行检查。 但是尽量不要使用类型转换,如果必须使用类型转换,则应该检查超类的设计是否合理。
包含一个或多个抽象方法的类本身必须被声明为抽象的。 但是,类即使不含抽象方法, 也可以将类声明为抽象类。
在 Java 中,只有基本类型 ( primitive types ) 不是对象, 所有的数组类型,不管是对象数组还是基本类型的数组都是扩展自Object的类对象。
为一个类编写equals方法的完美建议:
- 显示参数命名为otherObject
- 检测this与otheObject是否引用同一对象:
1
2 > if (this == otherObject) return true;
>
- 检测otherObject是否为null
1
2 > if (otherObject == null) return false;
>
- 检测this与otherObject是否属于同一个类:
如果equals的语义在每个子类有所改变,则使用getClass检测
1
2 > if (getClass() != otherObject.getClass()) return false;
>
如果所有子类使用相同的语义,即由超类决定相等的概念,则使用instanceof检测
1
2 > if (!(otherObject instanceof ClassName)) return false;
>
- 将otherObject转换为相应的类类型变量
1
2 > ClassName other = (ClassName) otherObject;
>
- 开始属性域的比较,使用==比较基本类型,使用Objects.equals比较对象
如果重新定义 equals 方法, 就必须重新定义 hashCode 方法。
强烈建议为自定义的每一个类增加toString方法。
对象包装器类(Integer,Long,Float,Double,Short,Byte,Character,Void,Boolean)是final类, 因此不能定义它们的子类。
自动装箱规范要求 介于 -128 ~ 127 之间的byte, short , int, char(0~127) 被包装到固定的对象中(ByteCache,ShortCache,IntegerCache,CharacterCache)。 因此
1 | Integer a = 1000; |
自动装箱拆箱陷阱
1 | Integer a = 1; |