Re:从零开始的java学习——类与对象

一些概念

类是现实世界中某些具有共同特征的实物的抽象概念;而对象是实际存在的个体。所以创建类的过程也可以称为“实例化”,而提取类的过程成为“抽象”。

OOA:面对对象分析(Object-Oriented Analysis)
OOD:面对对象设计(Obejct-Oriented Design)
OOP:面对对象编程(Obeject-Oriented Programming)

三大特征

封装(Enecapsulation)

1、变量访问修饰词用 private。
2、设置构造方法(可选)
3、为每个变量设置 getter() 和 setter() 方法(静态变量和实例变量对应静态方法和实例方法)

继承(Inheritance)

基本作用是代码复用,更重要的是这是方法覆盖和多态的基础。但是会导致代码耦合度高,修改父类会导致子类改变。

class Superclass {
}
class Subclass extends Superclass {
}

1、子类会继承父类的所有属性和方法,除了构造方法,但是实际上构造子类时会调用父类的构造方法。值得注意的是,此时调用的是父类的无参构造方法,因为父类的构造方法会比子类的构造方法先执行,而构造子类时传入的参数只给了子类的构造方法,所以调用父类的构造方法时是没有参数传入的。
2、父类中私有的成员和方法可以继承但子类不能访问,super 也不行。也就是说,子类可以完成父类的所有功能,但子类无论如何都不能直接使用继承过来的私有方法或查看私有变量
3、java 只支持单继承,不像 C++ 可以多继承。但可以通过连续继承达到类似的效果
4、Object 类是所有类的根类。

多态(Polymorphism)

编译时一种形态,运行时另一种形态。目的是用一个父类代表一系列子类,调用这些子类同名但不同内容的方法。这样可以保证扩展性的同时减少原代码的修改(OCP:对扩展开放,对修改关闭)。即面向抽象编程

// 向上转型upcasting
Superclass a = new Subclass(); // 父类型的引用允许指向子类型的对象

编译阶段:编译器认为 a 的类型是 Superclass,所以编译器在检查语法时,会在 Superclass.class 字节码文件中寻找被调用的方法并绑定。(编译阶段绑定属于静态绑定)所以,如果子类有自己独有的方法,这么做将会静态绑定失败,编译报错。
运行阶段:运行阶段,实际在堆内存中创建的 java 对象是 Subclass,所以调用方法时,真正运行的是 Subclass 对象的方法。(运行阶段绑定属于动态绑定)


为解决静态绑定失败问题,就需要使用向下转型

// 向下转型(downcasting)
Subclass b = (Subclass)new Superclass(); //子类型的引用允许指向父类型的对象

虽然可以解决编译问题(本质上是骗过了编译器),但是如果实际对象并没有可以被调用的对应方法,则运行时会报错java.lang.ClassCastException

【总而言之,编译看的是形式上的对象类型,运行看的是实际中的对象类型】


为了解决类型转化错误的问题,又需要新的运算符
instanceof:可以在运行阶段动态判断引用指向的对象的类型

x instanceof Myclass; // 结果只能是 boolean 类型
// 如果结果为 true,则表示 x 指向的对象是 Myclass 类型

属性

1、也叫成员变量,如果有修饰词 static 则为静态变量,可以通过类名访问;否则为实例变量,只能创建对象后访问。
2、成员变量如果不主动赋值,则在系统调用构造方法生成对象的时候会自动赋值,且缺省值全都是 0,而引用类型则都是 null。

方法

传递和引用

1、区别在于形式参数是基本参数类型还是引用参数类型,但本质其实是一样的, 都是将变量储存的值复制到被调用函数中
2、如果是基本数据类型,则其值为字面量,传参实际是复制了一份字面量进入了被调用的函数中,所以被调用函数中操作的局部变量是复制出来的字面量,与外部函数的变量无关。
3、而如果是引用数据类型,其值为对象的内存地址,传参同样是复制了变量的值——对象的内存地址进入被调用函数,但此时被调用函数将同样可以根据地址操作其指向的对象。

构造方法

MyClass a = new MyClass();
MyClass b = MyClass();

1、当一个类没有自定义构造方法时,系统会默认提供一个无参数的构造方法,该方法也被称为缺省构造器

MyClass a = new MyClass(); //此处 new 作用即调用构造方法,MyClass() 其实就是构造方法

2、构造方法没有返回值,方法名与类名一致,修饰符一般写 public(没有 static)。
3、构造方法可以重载,但一旦有自定义的构造方法,系统就不会提供默认构造方法,所以默认构造方法是不能参与重载的。

方法覆盖

子类继承父类的方法后,如果有需要修改,可以直接重写方法,并要求方法名、参数列表、返回值类型(严格来讲,基本数据类型必须一致,引用数据类型可以变得更小,但意义不大)等均一致。
访问权限不能更低,但可以更高
重写之后的方法不能比之前的方法抛出更多的异常,但可以更少
注意事项:
1、私有方法无法覆盖。注意方法覆盖和多态机制是联合使用的,方法覆盖就是为了配合多态。而父类的私有方法不能在类外部被调用,那么多态机制在运行阶段不能被实现;另外,就算在父类中实例化一个子类对象,并用子类的同名方法尝试触发多态机制(虽然这并没有什么实际意义),最后实现的依然是父类的效果。
2、构造方法不能被继承,所以不能被覆盖
3、方法覆盖只针对实例方法,静态方法覆盖没有意义。同样是因为方法覆盖和多态机制的联合关系,由于静态方法只关联类而不关联对象,所以不存在编译和运行上的差别,所以不存在多态。
方法覆盖并没有真的把父类的方法替换了,通过 super 依然可以调用,同理属性也可以。

修饰词

可以修饰类也可以修饰方法
public:共有访问,允许外部访问。例如 A 文件访问 B 文件中的 class 或者 class C 访问 class B 中的方法。
private:私有访问,不允许外部访问,即只能同级调用。
static
1、仅修饰属性和方法,有则为实例变量或实例方法,即对象相关;否则为静态变量或静态方法,即类相关。
2、实例变量或实例方法需要实例化对象后才能使用,且只能使用对象名引用的方式;静态则可以直接通过类名调用,同时也可以通过对象引用访问(不过不建议,容易混淆),即使引用是空指针。
3、静态变量在类加载时初始化,不需要 new 对象,而且静态变量储存在方法区,同样会有默认值
【静态变量储存在方法区,局部变量储存在栈,实例变量储存在堆】

this 指针

1、每个对象都有一个 this 指针,指向调用该方法的对象本身,它是一个变量,也是一个引用,保存当前对象的内存地址,储存在堆内存中对象的内部。
2、this 绑定的是对象,所以只能在实例方法中使用;反之,在静态方法中是不能使用 this 指针的。事实上在静态方法中,由于不需要构造对象,此时都没有在堆中分配内存,根本就没有 this 指针的存在。因此,静态方法中不能使用实例变量或实例方法,能使用实例变量或实例方法的只有实例方法
3、this 指针的意义:注意到在对象使用自己的实例变量时,无法通过引用访问,因为对象的引用是构造该对象的方法中的局部变量,对象本身是不能使用的,所以此时对象本身需要一个指针来访问自己。
4、当对象在使用自己的实例变量时,默认使用了该指针(一般情况下 this 可以省略)。
5、该指针一般用于在方法中区分同名的成员变量和传入的局部变量。一般变量使用满足就近原则,比如如果成员变量和方法内的局部变量重名(这是允许的),不加前缀的话,会默认使用局部变量。
6、通过当前的构造方法调用另一个本类的构造方法:

class MyClass {
	public MyClass(int i) {}
	public MyClass() {
		this(10);
	}
}

注意,构造方法必须是在同一个类中,另外 this() 的调用只能出现在构造方法的第一行,且只能出现一次。

super

代表的是当前对象中的父类型特征。与 this 的规则类似,例如同样绑定的是对象,不能出现在静态方法中。
super(),只能出现在构造方法的第一行,且只能出现一次,作用是调用父类的构造方法。如果既没有 this() 又没有 super()(super() 和 this() 不能共存),子类的构造方法中都会默认在第一行调用父类的无参构造方法。
super.:用于调用当前对象的父类特征,注意和 this 的区别,this 是用于区分是不是本对象的特征;而 super 是用于区分特征是本对象子类部分还是父类部分。所以当子类和父类有相同变量或方法时,如果要调用父类部分的变量或方法时,不能省略前缀。注意,父类私有的特征是访问不了的。
另外,与 this 不同,super 不是引用,不保存内存地址,也不指向任何对象

空指针异常

1、java 中无法直接操作堆内存中的地址(没有指针)。
2、假如一个引用变量的值是 null(即地址为空),强行使用其实例变量或实例方法将会编译报错(NullPointerException)。而如果是静态的变量或方法则不会
调用它的实例变量时,因为不知道地址,访问不了内存。
而调用实例方法时,虽然方法本身依然是从方法区加载然后压栈,但由于实例方法绑定的是对象实例而不是类,通常都会需要操作具体对象的实例变量,而这些变量又会储存在堆内存中,所以没有地址同样会编译报错。
3、垃圾回收器(GC)主要针对堆内存,其会自动回收堆内存中没有被任何引用指向的对象的内存空间。

代码块

静态代码块

static {
	java 语句;
	java 语句;
}

1、类加载时执行,只执行一次,且在 main 方法执行之前执行(在类的内部,但不在任何方法的内部)。
2、静态代码块可以有多个,一般按照自上而下的顺序执行。
3、作用是提供一个程序员在类加载时期操作的机会。
4、可以访问静态变量,因为静态变量也是在类加载时期加载的。但是注意,因为执行时机相同,所以执行先后由代码顺序决定,所以如果静态变量在代码块执行之后才定义,会编译报错:非法的前向引用。

实例代码块

{
	java 语句;
	java 语句;
}

1、每次实例化对象的时候执行,且在构造方法执行之前执行(在类的内部,但不在任何方法的内部)。
2、同样可以有多个,多个代码块也是自上而下执行。
3、作用是提供一个程序员在对象构造时期操作的机会。在有多个构造函数重载时,使用该代码块可以减少重复代码。
4、不能访问实例变量,因为此时构造函数还没有执行。

Tips

有顺序要求的 java 程序

1、方法体中的代码,自上而下执行。
2、一个类中的多个静态代码块,自上而下执行。
3、静态代码块和静态变量的定义,自上而下执行。

native 关键字

当源码中的一个方法以;结尾,并且修饰符列表有 native关键字:

private static native void registerNatives();

表示该方法底层调用了 C++ 写的 dll 程序( dll 动态链接库文件)。

打印引用

System.out.println(Myclass); // Myclass@十六进制数

直接输出引用的时候,会调用 toString()方法(从Object类继承的),输出格式为:类名@十六进制数,其中十六进制数为对象的地址(也就是引用的值)的哈希值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值