4.1 源文件的声明
package:指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。package 顶层包名.子包名;
import:引入指定包层次下所需要的类或全部类(.*)。import 包名.类名
源文件声明规则
package com.java.example;
import ....;
import ...;
public MyClass{ // 一个源文件中只能有一个 public 类且源文件的名称应该和 public 类的类名保持一致。
}
4.2 类和对象
Java程序是一系列对象的集合,这些对象通过调用彼此的方法来协同工作。
- 类:描述一类对象的行为和属性。
- 对象:类的一个实例,是实实在在的某个物体。
匿名对象:不定义对象的名称,直接调用对象方法。如new Person().shout();
。使用情形如下:
4.3.1 可变参数
Java一个方法只能指定一个可变参数,它必须是方法的最后一个参数,其可传入任意长度数组或任意个指定类型参数。数组仅可传入任意长度数组,不可为任意个指定类型参数。
public static void test(int a ,String[] books); // 这里的两种方法由于参数相同,不能重载
public static void test(int a ,String …books);
test(123,new String[]{"123","456","789"}); // 可以调用方法1和方法2
test(123,"123","456","789"); // 仅可调用方法2
test(123); // 仅可调用方法2
4.3.2 值传递机制
- 形参是基本数据类型:将变量的“数据值”传递给形参,函数内改变变量数据不改变原有数据值。
- 形参是引用数据类型:将变量的“地址值”传递给形参,函数内改变变量数据将改变原有数据值。
4.3.3 构造方法
当一个对象被创建时候,至少要调用一个构造方法来初始化该对象。所有的类都有构造方法,如果没有显式地为类定义构造方法,Java 编译器将为该类提供一个不带任何参数的默认构造方法。
默认构造方法的访问修饰符和类的访问修饰符相同。一旦定义了自己的构造方法,默认构造方法就会失效。父类的构造器不会被子类继承。
public class Person{
public Person(String name, int age, Date d) {this(name,age);…} // 调用重载的构造方法
public Person(String name, int age) {…}
public Person(String name, Date d) {…}
public Person(){…}
}
4.3.4 this关键字
调用当前类的属性、方法和构造器,如果在本类中未找到,会在父类中查找。当在方法内使用,其意为该对象的引用。
this(); //调用本类的无参数构造器,必须定义在类构造器首行
this(name); //调用本类的其他有参数构造器,必须定义在类构造器首行
this.name = name; // 调用本类属性,通常用this来区分同名属性和局部变量
this.speak(); // 调用方法
4.3.5 static关键字
有时我们希望无论产生多少对象,某些特定的内容仅保留一份,让所有实例共享,则使用static
关键字。
将这些属性设置为类属性,方法设置为类方法。类方法无需创建对象就可调用。static方法只能访问类的static属性或方法,不可访问非static结构,也不可使用this和super关键字。
4.3.6 代码块
代码块用于对类或对象进行初始化,仅可由static修饰。
- 静态代码块:
- 不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
- 非静态代码块:
- 可以调用非静态的属性和方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
程序中成员变量赋值的执行顺序:class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
- 默认初始化
- 显式初始化、多个初始化块依次被执行
- 构造器成员进行初始化操作
- 通过”对象.属性”或”对象.方法”给属性赋值
4.4 面向对象特征—封装
程序设计追求“高内聚,低耦合”,也就是说,隐藏对象内部的复杂性,只对外公开简单的接口:
- 高内聚 :类内部的数据操作细节由类自己完成,不允许外部干涉;
- 低耦合 :仅对外暴露少量的方法用于调用。
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
class的权限修饰符仅能用public和default。Java中通过定义private
属性与public
的get、set
方法来提高代码的可维护性,隐藏类内部的细节。
4.5 面向对象特征—继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,多个类无需再定义这些属性和行为,只要继承那个类即可。父类更通用,子类更具体。合理的使用继承会大大减少代码量,同时便于维护,提高代码复用性。
- 继承的特性:
- 子类拥有父类非 private 的属性、方法。子类使用
super
调用父类属性、方法。 - 子类可以拥有自己的属性(同名属性也可,不会覆盖父类属性)和方法,即子类可以对父类进行扩展。
- 重写方法仅可被声明为相同或更高权限,且返回值类型不可大于父类方法返回值类型。
- Java 的继承是单继承,final声明的类不可继承
- 子类不继承父类的构造方法的。如果父类存在无参构造器,系统会自动调用父类的无参构造器,否则必须在子类的构造方法中显式地通过
super
调用父类的构造方法或者通过this
调用本类的其他构造方法。 - 子类即父类,但父类不是子类。 | this | super | | —- | —- | | 访问本类中的属性,若本类没有则从父类中继续查找 | 直接访问父类中的属性 |
- 子类拥有父类非 private 的属性、方法。子类使用
重载(overloading) 是在一个类里面,方法名字相同,而参数个数、类型、类型顺序不同。、
重写(override)是子类中存在与父类中方法名字,参数与返回值均相同的方法。
方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
4.6 面向对象特征—多态
对象的多态性:父类的引用指向子类的对象,是一种运行时行为。
多态存在的三个必要条件:继承、重写、父类引用指向子类对象。作用是为了提高代码的通用性,常称作接口重用。
使用多态时,引用变量不能访问子类中添加的属性和方法,仅能调用子类的重写方法,成员变量不具备多态性。
4.7 Object类
Object
类是所有Java类的根父类。如果在类的声明中未指明其父类,则默认父类为java.lang.Object
。
public Object() //构造器
public boolean equals(Object obj) // 对象比较
public int hashCode() // 取得Hash码
public String toString() // 对象打印时调用
==
与equals
的区别:
基本数据类型没有区别。对于引用类型,==
比较内存地址,equals
默认也是比较内存地址,但可以重写。toString()
:String
与其它类型数据连接时,其他类型自动调用toString()
,可以根据需要在自定义类型中重写toString()
方法 ```java Date now = new Date(); System.out.println(“now=”+now); // 相当于“now=”+now.toString() System.out.println(now); // 相当于s1.toString()
public void test() { char[] arr = new char[] { ‘a’, ‘b’, ‘c’ }; System.out.println(arr);//打印abc,字符串底层也是使用字符数组实现的 int[] arr1 = new int[] { 1, 2, 3 }; System.out.println(arr1);//打印内存地址 double[] arr2 = new double[] { 1.1, 2.2, 3.3 }; System.out.println(arr2);//打印内存地址 }
<a name="iLQXm"></a>
## 4.8 抽象类与接口
<a name="aAjt0"></a>
### 4.8.1 抽象类
> 将易变的不确定部分抽象出来让子类去实现。
抽象类不能实例化对象,所以抽象类必须被继承。类的其它功能如,成员变量、成员方法、构造方法的访问方式和普通类一样。
`abstract` 声明**抽象类或抽象方法**,抽象方法只包含方法名,而没有方法体。`public abstract double computePay();`
如果一个类包含抽象方法,那么该类必须是抽象类。任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
<a name="YZDYi"></a>
### 4.8.2 接口
使用接口的情景:
1. 从几个类中派生出一个子类,继承它们所有的方法和属性。
1. 从几个类中抽取出一些共同的行为特征,而又非是不是的关系。
接口是一组规范,体现的是“能不能”的关系,而继承体现的是“是不是”的关系。类描述对象的属性和方法,接口则包含类要实现的方法。接口可以理解为一种特殊的类。
接口`interface`是抽象方法和常量值的集合,接口也可继承其他接口。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。接口可用来声明一个变量,成为一个空指针,引用一个以此接口实现的对象。<br />`class SubClass extends SuperClass implements InterfaceA{ }`
接口的特性:
- 接口没有构造方法,不能实例化对象;
- 接口支持多继承;
- 接口中每一个方法是隐式抽象的,无需自己指定为abstract;
- 接口中的变量会被隐式的指定为 public static final 变量,无需自己指定;
Java8中,可以为接口添加静态方法和默认方法。
- **静态方法:**使用 static 关键字修饰。仅可通过接口直接调用静态方法。
- **默认方法:**默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
子类继承了两个接口,若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法,会出现:**接口冲突。**解决办法就是实现类必须覆盖接口中同名同参数的方法,来解决冲突。
子类继承了父类和接口,接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:**类优先原则。**
<a name="w3tY8"></a>
### 4.8.3 抽象类和接口的区别
1. 抽象类中的方法可以有方法体,但是接口中的方法不行。
1. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
1. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
```java
[可见度] interface 接口名称 [extends 其他一个或多个接口名]
{ // 声明变量与抽象方法 }
在Java开发中90%使用的都是接口,抽象类往往只是实现一个过渡。如果你拥有一些方法并且想让它们中的一些有默认实现,使用抽象类。如果想实现多重继承,那么你必须使用接口,让子类强制实现。
// 调用接口的默认方法
Filial.super.help(); // 接口名.super.方法名;
4.9 内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整结构又仅为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
- 成员内部类:直接声明在外部类内,与成员变量同级,有访问限制符
- static成员内部类
- 非static成员内部类
- 局部内部类:声明在方法内、代码块内、构造器内(无访问修饰符)
成员内部类可以调用外部类的所有成员;声明为static时不能再使用外层类的非static的成员变量。可以声明为abstract类 ,因此可以被其它的内部类继承;也可以声明为final,表示此类不能被继承。
注意:
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
- 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
局部内部类只在声明它的方法或代码块中使用,而且是先声明后使用,其作用域、地位、使用方式均与局部变量相同。// 实例化成员内部类对象
// 静态成员内部类
Person.Dog dog = new Person.Dog();
// 非静态成员内部类
Person person = new Person();
Person.Cat cat = person.new Cat();
// 内部类调用外部类的方法
//内部类与外部类重名
Person.this.name
//内部类与外部类无重名
name
// 返回一个实现了Comparable接口的类对象
public Comparable getComparable(){
// 方法1:创建一个实现了Comparable接口的类:局部内部类
class MyComparable implements Comparable{
public int compareTo(Object o){
return 0;
}
}
return new MyComparable();
// 方法2:
return new Comparable(){
public int compareTo(Object o){
return 0;
}
}
}