第一章
- 标识符和JavaBean
和大多数的语言一样,标识符由字母、数字和下划线组成,第一个字符不能为数字。
Javabean的标准参考阿里巴巴开发手册,变量和方法的命名要有自说明功能,对于实例变量,使用标准的setter和getter - 声明类
一个.java文件只能有一个public类且与.java文件同名,一个.java文件可以有多个类,类声明形式:class Xxx{}
- 声明接口
创建一个接口,是在定义一份契约,除非这个类是抽象类,否则所有实现该接口的类都需要实现这些契约,一个类可以实现多个接口,一个接口也可以继承多个接口,(jdk1.6)接口中只能含有抽象方法,声明的变量也是常量并且都是默认是、只能是public的(常量默认被static final修饰)。接口声明:
interface Xxx{}
- 声明类成员
通常我们将方法和实例变量(非局部的)统称为成员,方法常称为实例方法。
1)访问控制
可以将类当作一个细小的系统,而系统通常有用户,不同的用户有不同的权限,系统通过账号来识别权限,而类是通过访问修饰符实现。对于一个类来讲,如果它对其使用者而言是不可见的,即无法谈论操作类的内部,就像这个系统你见都见不到怎么登陆?
访问修饰符有:public、protected、private。无访问修饰符,则为包权限,包权限和protected很像,只有在继承关系中才能有所区别。其中的区别:
public:被修饰的方法、实例变量所有类都是访问
private:被修饰的方法、实例变量只能在该类内部进行访问
protected:被修饰的方法、实例变量可以在类内部访问,同时也能被子类访问
包权限:可以将包理解为目录,方法、实例变量可以在类内部访问,同时也能被子类访问,除此之外在同一个目录下的类也能进行进行访问。
上面所说的有点抽象,尤其是包权限,可以这么想象,有两个类,其中一个为
public class A{
int age;//没有修饰符,所以为包权限
}
另一个
public class B{
public static void main(String[] args){
A a = new A();
System.out.println(a.age); //这个可以进行访问吗?
}
}
假如A、B的目录结构如下,可以访问
-packageXxx
-A
-B
下面则否
-packageXxx1
-A
-packageXxx2
-B
2)包权限和protected的区分要记
//包结构:
-packageXxx1
-A
-packageXxx2
-B
public class A{
int age;//没有修饰符,所以为包权限
}
public class B extends A{
public static void main(String[] args){
System.out.println(age);//没有问题
A a = new A();
System.out.println(a.age); //此处编译报错
}
}
看到这里大概能稍微理解他们的区别了,age虽然是包权限,但是为什么可以在不同包下被B访问呢,因为其不是通过简单的.
访问,而是通过继承(所以对于“访问权限”这一词而言,描述的是简单访问的权限),如果使用protected修饰age,则上述没有问题。
3)声明枚举
枚举可以看成是已经规定好内容的类,基本构成是常量,这些常量代表的含义类似。
第二章 面向对象
- 封装
封装是一种抽象的概念,目的是为了实现类的单一职责,从而实现松耦合和高内聚,以方便程序的维护。 - 继承和多态
window操作系统中有用户的概念,其职责是用于控制用户对操作系统操作权限。每一个用户的操作权限是可以不同的,但绝大部分的权限一样,如果没建立一个用户都需要为其分配一次权限(数百个多选框摆在你面前),那将是痛苦的,所以用户组这一概念产生,用于管理一组拥有类似权限的用户,这样一来,在创建一个用户时,将其放置在对象用户组即可。其中继承的概念与此时相似的,只是继承复用的时代码。
通常我们将某个用户归属与某个用户组时,例如权限管理员和系统管理员组,就会说权限管理员是系统管理员,这就是继承中的IS-A测试,只有通过IS-A测试的Java对象才可以谈论多态。这个可以类比文件管理员和权限管理员,系统管理员组提供了组员可以操作文件的能力,他们都可以对文件进行操作,但是操作时由于权限的不同,系统执行的结果就会不同,例如权限管理员要删除文件,系统会拒绝,文件管理员则可以删除。 - 重写和重载
因为通过IS-A测试的父类引用可以引用子类的对象,不同子类内部的实现细节可以不同,所以会出现同一个引用,调用同一个方法,但是产生不同结果的现象,这个现象称为多态。在父子类间实现多态的方式是通过方法重写,即子类重写父类方法。而在单个类中实现多态的方式是方法重载。
1)方法重写
即子类覆盖父类的方法实现,所以覆盖的方法必须和原来的名称、参数列表和返回值一样(通过IS-A测试)。但是其不能被final、static修饰,并且其访问权限不能比父类的严格、抛出的异常不能比父类的宽泛(只能更少更精确,但是如果父类没有抛出异常,这个不做限制)。
2)方法重载
在一个类中有两个或多个方法的名称一样,但是方法的参数列表不同,这个不同体现在参数的类型(对应的位置)、参数的个数。每一个重载的方法可以当作是一个新的方法,所以没有方法重写那么多条条框框。
方法重写实现的是运行时的多态,方法重载实现的是编译时的多态
- 合法的返回值
需要重点记住的是子类在重写父类方法时,覆盖方法的返回值只要通过IS-A测试就是合法的 - 构造函数和实例化
1)构造函数
在编写一个类时,如果没有显示的为类提供一个构造函数,则在编译时,jvm会提供一个默认的构造函数;如果提供了构造函数(无论何种形式),jvm不再提供默认构造函数,默认构造函数如:
class Xxx{
//默认构造器,无参的
Xxx(){
}
}
容易造成的误区是当你仅定义了一个有参构造器后认为还能使用
Xxx x = new Xxx();//编译报错
2)实例化
一个类在进行实例前,会先实例化其超类,通常是通过一个隐式的super();
进行调用父类构造器的。实例化过程有简单的也有复杂的,在夹杂静态代码块和初始化块的情况下,实例化变得复杂,它们的流程如下:
首先我们要知道一点:
静态代码块在类加载时便会执行,即其只执行一次;
初始化代码块在构造器执行前执行,即每次实例化都会执行一次。
实例化时类的加载顺序上,父类优先于子类。
执行顺序:
父类静态代码块->子类静态代码块->父类初始化块->父类构造器->
子类初始化块->子类构造器->构造完成
- 静态成员
静态成员是属于类的,由所有该类的实例共享,使用静态变量时需要对变量进行初始化,静态方法中不能调用非静态的方法和变量
第三章 赋值
- 前提知识
实例变量和对象是驻留在堆上的,局部变量驻留在栈上。
每调用一个方法,该方法就会入栈,方法调用结束时就出出战。
在方法入栈期间生成的局部变量和引用在方法出栈后而被清除。 - 静态变量、实例变量与局部变量的赋值
- 静态变量在使用前必须被初始化
- 实例变量如果没有被显示的初始化,则会为其分配默认值
- 局部变量没有初始化,在保证不使用它的前提下,编译不会报错,如果使用未初始化的局部变量编译将会报错。局部引用同理,并不会为一个未初始化的引用主动分配一个null值,所以将会出现
Xxx xxx;
if(xxx == null) {// 编译报错
System.out.println();
}
- 向方法传递变量
在Java中,无论向方法传递的是基本类型变量还是引用类型变量,都是使用值传递的方式。
- 对于基本类型变量,会对变量值进行拷贝,方法形参获得的是实参的一个副本,所以在方法上修改,并不影响方法外的;
- 对于引用类型变量,向方法形参传递的是实参的
位模式
副本,而位模式
可以理解为保存着寻找实际对象的方法,所以在方法上对使用形参进行操作,实际上是对同一个对象进行操作(因为寻找对象的方法一样,所以找到的对象相同),故而会影响方法外的对象。
- 包装类和拆装箱
- 包装类的出现
因为基本类型变量本身提供的操作有限,而我们通常会频繁的对基本类型进行转换,这些工作如果全部手动完成,将会非常影响效率。所以jvm提供了包装类,并且提供了一种基本类型与包装类型间相互转换的机制(自动拆装箱机制),而这些包装类提供了许多功能,大大简化了数据的处理过程。 - 基本类型与之对应的包装类型
基本类型 包装类型
int Integer
short Short
long Long
char Character
byte Byte
boolean Boolean
float Float
double Double
- 拆装箱示例
Integer x = 1; //自动装箱
int y = x; // 自动拆箱
- 重载
- 导致重载难于处理的3个因素:
- 加宽(对于基本类型变量,存在隐式转换,转换后表示的范围为更加宽泛)
byte y = 5;
int x = y; //y隐式转换为int
- 自动装箱
- var-arg
- 加宽和装箱单独使用下的规则
5.1 加宽先于装箱、var-arg执行,加宽遵循最小范围的加宽
static void go(Integer x){
System.out.println("int");
}
static void go(long x){//此处是long而不是Long
System.out.println("long");
}
public static void main(String[] args){
int x = 5;
go(x); //此处输出的是long
}
static void go(int x, int y){
System.out.println("int int");
}
static void go(byte... x){
System.out.println("byte...");
}
public static void main(String[] args){
byte x = 5;
go(x, x); //此处输出的是int int
}
5.2 装箱先于var-arg执行
static void go(Byte x, Byte y){
System.out.println("Byte Byte");
}
static void go(byte... x){
System.out.println("byte...");
}
public static void main(String[] args){
byte x = 5;
go(x, x); //此处输出的是Byte Byte
}
5.3 引用变量的加宽,只要通过IS-A测试,则可以加宽
- 加宽和装箱组合使用下的规则
- 先装箱
- 再加宽,能否加宽成功,取决于IS-A测试
- 可以组合使用var-agr与加宽或装箱
static void wide_vararg(long... x){
System.out.println("long...");
}
static void box_vararg(Integer... x){
System.out.println("Integer...");
}
public static void main(String[] args){
int x = 5;
wide_vararg(x, x); // 输出long...
box_vararg(x, x); // 输出Inetger...
}