面向对象
构造器
- 构造器与类同名。
- 每个类可以有一个以上的构造器。
- 构造器可以有0个、1个或1个以上的参数。
- 构造器没有返回值。
- 构造器总是伴随着new操作一起使用。
- 默认构造器指的是没有参数的构造器。
- 如果类没有构造器,系统会提供一个默认的构造器。这个默认构造器将所有实例域初始化为默认值。仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器。如果类中提供了至少一个构造器,但是没有提供默认的构造器,则在构造对象如果没有提供构造参数就会被视为不合法。
访问修饰符
public对所有类可见。private对本类可见。protected对本包和所有子类可见。- 默认对本包可见。
面向对象三大特征
封装
封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。就好像我们看不到挂在墙上的空调的内部的零件信息(也就是属性),但是可以通过遥控器(方法)来控制空调。如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。就好像如果没有空调遥控器,那么我们就无法操控空凋制冷,空调本身就没有意义了(当然现在还有很多其他方法 ,这里只是为了举例子)。
继承
不同类型的对象,相互之间经常有一定数量的共同点。例如,小明同学、小红同学、小李同学,都共享学生的特性(班级、学号等)。同时,每一个对象还定义了额外的特性使得他们与众不同。例如小明的数学比较好,小红的性格惹人喜爱;小李的力气比较大。继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
多态
多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
一个对象变量可以引用多种实际类型的现象被称为多态。有一个用来判断是否应该设计为继承关系的简单规则,这就是“is-a”规则,它表明子类的每个对象也是超类的对象。例如,每个经理都是雇员,因此,将Manager类设计为Employee类的子类是显而易见的,反之不然,并不是每个雇员都是经理。
多态的特点:
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法;
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
继承
- 如果没有明确指出超类,Object就被认为是这个类的超类。
- 覆盖一个方法时,子类方法不能低于超类方法的可见性。若超类是public,则子类必须为public。
- 如果子类的构造器没有显示地调用超类的构造器,则将自动调用超类默认的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中又没有显示地调用超类的其它构造器,则编译器将报告错误。
类之间的关系
- 依赖(use-a)
- 聚合(has-a)
- 继承(is-a)
抽象类
- 包含一个或多个抽象方法的类必须声明为抽象的。
- 除了抽象方法,抽象类还可以包含具体数据和具体方法。
- 类即使不含抽象方法,也可以将类声明为抽象类。
- 抽象类可以实现多个接口。实现接口可以不实现方法,但必须声明为abstract。
- 抽象类不能被实例化。可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。
interface IBook {
int page();
}
abstract class abstarctBook implements IBook {
}interface IBook {
int page();
}
abstract class abstarctBook implements IBook {
public abstract int page();
}接口
- 接口中的所有方法都自动是
public。接口中的域将被自动设为```public static final``。 - 在实现接口时,必须把方法声明为public,否则,编译器将认为这个方法的访问属性是包可见行性。
- 接口不是类,不能是使用new实例化一个接口。
- 接口变量必须引用实现了接口的类对象。
Java8可以用default关键字在接口中定义默认方法。
对象与对象引用
String str = new String("HelloWorld");通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。
- 右边的"new String",是以String类为模板,在堆空间里创建一个String类对象。
- 末尾的"()"意味着,在对象创建后,立即调用String类的构造函数,对刚生成的对象进行初始化。
- 左边的"String str"创建了一个String类引用变量。可以用来指向String对象的对象引用。
- "="操作符使对象引用指向刚创建的那个String对象。
我们可以把这条语句拆成两部分:
String str;
sgtr = new String("HelloWorld");效果是一样的。这样写,就比较清楚了,有两个实体:一是对象引用变量,一是对象本身。
方法传递
总是采用值调用。方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。
方法得到是对象引用的拷贝,对象引用及它的拷贝同时引用一个对象。
- 一个方法不能修改一个基本数据类型的参数。
- 一个方法可以改变一个对象参数的状态。
- 一个方法不能实现让对象参数引用一个新的对象。
重载
相同的方法名,不同的参数类型。
初始化顺序
调用构造器的具体步骤:
Static初始化仅发生一次,在Class对象首次载入的时候。
所有数据域被初始化为默认值。
按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块。
如果构造器第一行调用了第二个构造器,则执行第二个构造器主体。
执行这个构造器主体。
- 在类第一次加载的时候,将会进行静态域的初始化。与实例域一样,除非将它们显示地设置成其它值,否则默认的初始值是0、false或null。所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行。
- 如果父类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
- 如果类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
- 将类的成员赋予初值(原始类型的成员的值为规定值,例如int型为0,float型为0.0f,boolean型为false;对象类型的初始值为null)
- 如果构造方法中存在this()调用(可以是其它带参数的this()调用)则执行之,执行完毕后进入第7步继续执行,如果没有this调用则进行下一步。(这个有可能存在递归调用其它的构造方法)
- 执行显式的super()调用(可以是其它带参数的super()调用)或者隐式的super()调用(缺省构造方法),此步骤又进入一个父类的构造过程并一直上推至Object对象的构造。
- 执行类申明中的成员赋值和初始化块。
- 执行构造方法中的其它语句。
简单概况如下:
- 父类的静态成员赋值和静态块
- 子类的静态成员和静态块
- 父类的构造方法
- 父类的成员赋值和初始化块
- 父类的构造方法中的其它语句
- 子类的成员赋值和初始化块
- 子类的构造方法中的其它语句
class Language {
static {
System.out.println("language static block");
}
{
System.out.println("language block");
}
public Language() {
System.out.println("language constructor");
}
public Language(String name) {
System.out.println(name);
}
}
public class Python extends Language {
private String name;
public Python(String name) {
this.name = name;
System.out.println("python constructor");
System.out.println(this.name);
}
{
System.out.println("python block");
}
static {
System.out.println("python static block");
}
public static void main(String[] args) {
new Python("hello python");
}
}
public class Test {
public static void main(String[] args) {
new Python("hello python");
}
}运行结果:
language static block
python static block
language block
language constructor
python block
python constructor
hello python关键字
this
- 是引用隐式参数。
- 调用本类其他的构造器。
- 调用本类其他构造方法时,必须放在首行。
super
- 调用父类的方法,
- 调用父类的构造器。
- 调用父类中的其他构造方法时,必须处于构造器的首行。
final
如果将一个类声明为Final,只有其中方法自动成为Final,而不包括域。
- 修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲,我们必须在声明时或者构造方法中对它赋值;
- 修饰方法参数,表示在变量的生存期中它的值不能被改变。
- 修饰方法,表示该方法无法被重写。
- 修饰类,表示该类无法被继承。
public static void finalTest(final int num, final String str){
num = 5; //Cannot assign a value to final variable 'num'
str = "final";
}static
- static可以修饰变量,方法。
- 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。
- 在类被加载的时候,就会去加载被static修饰的部分。
- 被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。
静态变量
被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象,没有被static修饰的成员变量叫做实例变量,说明这个变量是属于某个具体的对象的。
静态变量和实例变量的区别
- 实例变量:每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
- 静态变量:静态变量由于不属于任何实例对象,是属于类的,所以在内存中只会有一份,在类的加载过程中,JVM为静态变量分配一次内存空间。
静态方法
被static修饰的方法也叫做静态方法,因为对于静态方法来说是不属于任何实例对象的,那么就是说在静态方法内部是不能使用this的,因为既然不属于任何对象,那么就更谈不上this了。由于他在类加载的时候就存在了,它不依赖于任何实例,所以static方法必须实现,也就是说他不能是抽象方法abstract。
Caution
Illegal combination of modifiers: 'abstract' and 'static'
静态块
被static修饰的代码块,我们称之为静态代码块,静态代码块会随着类的加载一块执行,而且他可以随意放,可以存在于该了的任何地方。
class staticTest{
static{}
static{}
}静态内部类
非静态内部类依赖于外部类的实例,而静态内部类不需要。
静态内部类不能访问外部类的非静态的变量和方法。
public class OuterClass {
class InnerClass {
}
static class StaticInnerClass {
}
public static void main(String[] args) {
// InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
StaticInnerClass staticInnerClass = new StaticInnerClass();
}
}静态导包
静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。
import static java.lang.Integer.*;
public class Test {
public static void main(String[] args) {
System.out.println(MAX_VALUE); // Integer.MAX_VALUE
System.out.println(max(1,2)); // Integer.max()
}
}- 静态只能能调用static变量,static方法。不能以任何形式引用this、super。
- 非静态既可以访问非静态的,也可以访问静态的。
Caution
Non-static field 'x' cannot be referenced from a static context
'x.this' cannot be referenced from a static context