在循环中检测2个浮点数是否相等,要格外小心,由于舍入误差,最终可能得不到精确的值。
类名:字母开头,后跟字母或数字的任意组合(不能使用保留字)【通常以大写字母开头】
定义枚举类:
enum Size{S,M,L,XL}
Size s1 = Size.S;
Size s2 = Size.L;
一个字符串与一个非字符串凭借,后者被转换成字符串,Java对字符串中的代码单元和代码点从0开始。
equalsIgnoreCase()方法,不区分大小写
定义类,读取密码的时候,使用Console类
运算符 | 描述 |
---|---|
>> | 高位补符号 |
>>> | 高位补0 |
类型转换:
转换方式 | 描述 |
---|---|
强制类型转换 | double->float->long->int |
自动转换 | int->long->float->double |
2个数值进行二元操作,先将2个操作数转换为同一种类型,采用自动转换方式
switch语句:
int choice = 0;
switch (choice){
case 1:
do something
break;
case 2:
do something
break;
default:
do something
break;
}
case标签可以是char,byte,short,int,枚举常量,字符串字面量
关于break语句:
带标签的break语句,标签必须放在希望跳出的最外层循环之前,且必须紧跟冒号
here:
while (true){
do something
break here;
}
//跳出后在这里继续执行
标签可以放在任何位置,如if语句外等。
java.math包:
类 | 描述 |
---|---|
BigDecimal | 人员精度的浮点数运算 |
BigInteger | 任意精度的整数运算 |
数组:
数组类型 | 初始化值 |
---|---|
数字数组 | 0 |
boolean数组 | false |
对象数组 | null |
数值型数组排序,可以使用Arrays.sort(),这是优化的快速排序
一旦数组创建,就不能在改变它的大小了
初始化匿名数组的方式:new int[]{1,2,3,4}
不规则数组:每一行的个数都不同,要单独创建每一行的数组
数组的拷贝:
- 浅拷贝(引用),2个变量将引用同一个数组
- 深拷贝(内容),
int[] a = Arrays.copyof(b,b.length);
b.length 是新数组的长度。通常深拷贝用来增加数组的大小
对象的拷贝:
- 浅拷贝,还是指向同一段空间
- 深拷贝:克隆,但是默认的对象克隆还是浅拷贝,并没有克隆包含在对象中的内部对象
不要编写返回引用可变对象的访问器方法(这是浅拷贝,原来的对象会跟这返回的对象一起改变)
如果需要返回一个可变对象的引用,应该首先对它进行克隆(对象包含可变对象,必须进行深拷贝)
如果需要返回一个可变数据域的拷贝,应使用clone
对象
- 对用户公开特定的功能部分
- 隐藏实现部分
final实例域必须在构造时初始化,后面的操作中不能再修改
构造一个final的数组,数组的引用不可变,但数组中的值可变:
final int[] a = new int[1];
//a[0]的值可以改变
封装:
将数据和行为组合在一个包中并对对象的使用隐藏了数据的实现方式
关键:绝对不能让类中的方法直接方法访问其他类的实例域
Tips:如果不经过方法调用就能够改变对象状态,说明封装性被破坏了
类间关系
- 依赖(uses-a):一个类的方法操作另一个类的对象,这个类依赖另一个类
- 聚合(has-a):类A的对象包含类B的对象
- 继承(is-a):特殊与一般的关系
继承的子类具有父类的全部属性和方法
对象变量和对象的区别:一个对象变量(存储的只是引用)并没有实际包含一个对象,而仅仅引用一个对象
文件名必须与public类的名字相匹配,在一个源文件中只能有一个公有类,但可以有任意数目的非公有类
参数传递
Java程序设计语言总是采用按值调用,即方法得到的是所以参数的一个拷贝
方法不能修改传递给它的任何参数变量的内容
编译器如何确定重载的方法?重载解析
通过各个方法各处的参数类型与特定方法调用所使用的值类型匹配
方法的签名:
- 方法名
- 参数类型
仅当类没有提供任何构造器,系统才会提供一个默认的构造器(啥也没有的构造器,哈哈哈哈),如果类中提供了至少一个构造器,但没提供午餐的构造器,则在构造新对象时,如果没有提供参数就会被认为是不合法的。
类在第一次加载的时候,会对静态域进行初始化。所有的静态初始化语句和静态初始化块都依照类定义的顺序执行
import 导入静态方法和静态域的功能:
import static java.lang.System.*;
public class StaticImport {
public static void main(String[] args) {
out.println("Hello!");//这样就不需要使用类名进行静态方法的调用了
exit(0);//这样就不需要使用类名进行静态方法的调用了
}
}
关于javadoc(/** 后内内容),它会从下面的这几个特性中抽取信息:
- 包
- 公有类和接口
- 共有的和受保护的构造器和方法
- 共有的和受保护的域
应该为上面这几个部分写注释,注释应该放在所描述的特性前面
自定义类设计技巧
- 数据私有
- 数据初始化
- 不要在类中使用过多的基本类型
- 不是所有的域都要独立的域访问器和更改器
- 将职责过多的类分解
- 类名和方法名能够体现它们的职责
继承
- 继承已存在的类就是复用这些类的方法和数据域,在此基础上添加新的方法和域
- 在通过扩展超类定义子类的时候,仅需要指出子类与超类的不同之处
子类不能访问父类的私有域,所以必须在父类的构造器对这部分私有域进行初始化
- 使用super调用构造器的语句必须是子类构造器的第一句、
- 如果子类构造器没有显式调用超类构造器,则自动调用超类默认的无参构造器
- 如果超类没有无参构造器,并且在子类的构造器中没有显式调用超类的其他构造器,则编译器会报错
this & super
名称 | 描述 |
---|---|
this | 隐式参数、调用该类其他的构造器 |
super | 调用超类方法,调用超类构造器 |
多态
一个对象变量可以指示多种实际类型的现象称为多态
在运行时能够自动地选择调用哪个方法的现象称为动态绑定:
- 获取所有可访问的同名方法
- 重载解析不确定的方法
调用的方法用来于隐式参数的实际类型,并在运行时实现动态绑定
静态绑定:private static final 的方法或构造器, 编译器可以准确的调用
如果一个方法没有被覆盖并且很短,编译器可以优化为内联,例如:e.getName()
被优化为e.name
域
置换法则:程序中出现超类的任何地方都可以用子类对象置换
在Java中对象变量是多态的,父类变量可以引用父类对象也可以引用任何一个子类对象,不能将父类对象的引用赋值给子类
在Java中,每个对象变量都属于一个类型,类型描述了这个变量所引用的以及能够引用的对象类型
对象进行类型转换的唯一原因:暂时忽视对象的实际类型使用对象的全部功能
- 对象进行类型转换只能在继承层次内进行类型的转换(即父类转换为子类)
- 在转换前使用instanceof进行检查
对象中的equals方法具有如下特性:
- 自反性
- 对称性
- 传递性
- 一致性
- 对于任意非空引用x,x.equals(null)为false
基本类型和包装类
所有基本类型都有一个与之对应的包装类(包装器)
Number->(Integer、Long、Float、Double、Short、Byte)
Void包装类型是什么鬼,之后研究一下
对象的包装器类时不可变的,一旦构造了包装器,就不允许更改包装器在其中的值,包装器类是final,不能定义它们的子类
操作 | 内部实现 |
---|---|
自动装箱 | list.add(s); ==> list.add(Integer.valueof(s)); |
自动拆箱 | int n = list.get(i); ==> int n = list.get(i).intValue(); |
在算术表达式中也能够自动装箱/拆箱
参数数量可变的方法:
public PrintStream printf(String fmt, Object... args){
return format(fmt,args);
}
其中Object… args,表示Object[]数组,如果调用的对象提供的是整型数组或基本类型的值,自动装修功能把它转换成对象
运行将一个数组传递给可变参数方法的最后一个参数
自定义参数可变的方法:
public double max(double... values){
do something
return
}
枚举
枚举类型的实例固定,不构造新对象,在比较2个枚举类型时,永远不需要调用equals方法,直接使用“==”
枚举类型定义如下:
public enum classname {enmuA,enumB};
在枚举类型中可定义构造器方法和域,如下
private enum Size {
SMALL("S"),MEDIUM("M"), LARGE("L"), EXTRA_LARGE("Xl");
private String abbreviation;
private Size(String abbreviation){
this.abbreviation = abbreviation;
}
public String getAbbreviation(){
return abbreviation;
}
}
内部类
- 接口:主要用来描述具体数目功能,而并不提供每个功能具体的实现
接口中的域自动为public static final
接口中的方法自动为public不必提供关键字
接口中能定义常量,但不能有实例域
不能实现方法,只能定义
在实现接口时,必须把方法声明为public,接口不是类,不能用new运算符实例化,但可以声明接口类型的变量(这和抽象类很像)
- 内部类:定义在另一个类的内部,其中的方法可以访问包含它们的外部类
- 代理:实现任意接口的对象
内部类是定义在另一个类中的类:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据
- 内部类可以对同一个包中的其他类隐藏起来
- 当想要定义一个回调函数且不想编写大量代码,使用匿名内部类比较便捷
内部类即可以访问自身的数据域也可以访问创建他们的外围类对象的数据域
内部类对象总有一个隐式引用它指向了创建它的外围类对象,这个引用在内部类的定义中是不可见的
外围类的引用在构造器中设置,编译器修改了所有内部类的构造器添加一个外围引用的参数
编译器为类生成一个默认的构造器:
public Inner(Outer out){
Outer = out;//Outer不是Java的关键字,它相当于OuterClass.this
}
- 将内部类声明为privated这样只有外围类才能够构造内部类对象
- 只有内部类可以是私有的,常规类只可以具有包可见性或公有可见性
- 在外围类的作用域之外,引用内部类:
OuterClass.InnerClass
在方法中定义局部内部类:
- 局部类不能用public 或private访问说明符声明,它的作用域被限定在声明这个局部类的块中
- 局部类有宇哥优势可以对外界世界完全隐藏起来,除了该方法没有任何方法直到该类存在
- 与其他内部类相比,局部类的方法只能引用定义为final 的局部变量
静态内部类:
- 使用内部类只是为了 把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象,此时可以将内部类声明为static,以便取消产生的引用
- 只有内部类可以声明为static