我上大学的时候不爱学习,爱打游戏,每次都临考试前一个月疯狂抱佛脚,抱得佛估计都怕了,印象最深的是我大二学期的 Java 课考了 60 分,别看我及格了但也是极其危险,笔试前又去抱佛的脚,结果答得一般般,上机考试就更别提了,瞎写了几行,编译器都快看不下去了... 估计能考及格也是老师高抬贵手,当时找过系里老师两三次,就差提果篮去老师家上门了。
有时候经常会想,假如时光能倒流,让自己教大学时期学不进去的自己计算机、编程这类的知识,自己能不能学进去,考试的时候还会不会挂科,是老师当时照本宣科根本吸引不了我,还是我当时就没心思学习。
于是我特地总结了下当时大学觉得最难的面向对象的知识,由于这部分知识实在太多我分成了基础和进阶篇,我确认大学教的时候没这么多,这次先上基础篇,反射、各种内部类、抽象类、枚举、接口什么的下期再讲,大纲如下:
类是用来描述同一类事物的,可以在类中定义任意数量的、不同类型的变量,作为这一类事物的属性。这种属性叫做成员变量(member variable),除了类的属性外,类还可以有一组描述自己行为的方法(method)。其实类就相当于我们自己定义了一种数据类型。
比如下面我们定义了一个代表商品的类:
public class Commodity {
String name;
String id;
int count;
double price;
}
public class代表这个类是公共类(公共类名必须和文件名相同),商品类里定义了四个成员变量用来代表商品的四个属性--商品名、商品标识、商品数量和商品价格。
类的定义,其实就是创建了一个模版。描述了一种我们需要的数据类型。
初探类和对象
从数据类型的角度看,类就是我们根据自己的需要,创建了一种新的数据类型。所以类也叫做“自定义类型”。 一个 Java 程序中不允许类同名。
而对象(object)是类这个“自定义类型”的具体实例(instance)。
后面可能有时候会说对象、有时候会说类的实例,记住咱们要表述的其实是同一个东西,不必太刻意区分。
在 Java 中可以使用 new 操作符创建一个类的对象。在使用 new 创建的对象中,类中定义的那些成员变量都会被赋以其类型的初始值。看下面这个使用类对象的例子:
public class UserClassCase {
public static void main(String[] args) {
// 创建一个Commodity类的实例,用变量m1指向它。
Commodity m1 = new Commodity();
// 使用点操作符,给m1指向的实例赋值。
m1.name = "茉莉花茶包 20 包";
m1.id = "000099518";
m1.count = 1000;
m1.price = 99.9;
// 创建另外一个Commodity类的实例,用变量m1指向它。
Commodity m2 = new Commodity();
m2.name = "可口可乐 330ml";
m2.id = "000099519";
m2.count = 1000;
m2.price = 3.0;
// 卖出一个商品1
int m1ToSold = 1;
System.out.println("感谢购买" + m1ToSold + "个" + m1.name + "。商品单价为"
+ m1.price + "。消费总价为" + m1.price * m1ToSold + "。");
m1.count -= m1ToSold;
System.out.println(m1.id + "剩余的库存数量为" + m1.count);
// 卖出3个商品2
int m2ToSold = 3;
System.out.println("感谢购买" + m2ToSold + "个" + m2.name + "。商品单价为"
+ m2.price + "。消费总价为" + m2.price * m2ToSold + "。");
m2.count -= m2ToSold;
System.out.println(m2.id + "剩余的库存数量为" + m2.count);
}
}
引用类型
引用类型是使用 Java 时经常被提到的一个名词,Java 中的数据类型分为:基本数据类型和引用数据类型。上节说到变量的名和实,有这样一个概念 :
- 变量的名指代一个供程序访问的内存地址,变量的实就是地址指向的内存里的值。
那么引用数据类型和基本数据类型的差异在,基本数据类型变量的值,就内存地址里存放的值,而引用数据类型的变量值还是一个地址,需要跳到“值地址”对应的内存才能找到实例。
上面说的就是引用类型的实质,引用类型是Java的一种内部类型,是对所有自定义类型和数组引用的统称,并非特指某种类型。
下面看个例程加深下理解: 例程中 m1 是一个Commodity 类型的引用,它只能指向 Commodity 类型的实例,引用数据类型的变量包含两部分信息:类型和实例。也就是说,每一个引用数据类型的变量(简称引用),都是指向某个类( class /自定义类型)的一个实例/对象(instance / object)。不同类型的引用在 Java 的世界里都是引用。
引用的类型信息在创建时就已经确定,可以通过给引用赋值,让其指向不同的实例,比如 m1 就是 Commodity 类型的引用,可以通过赋值操作让它指向不同的 Commodity 类型实例。这个其实很好理解,就跟把一个整型的值赋值给浮点型的变量在 Java 里是不被允许的一样,基础类型也是相同类型的值才能对变量进行赋值操作。
public class ReferenceAndPrimaryDataType {
public static void main(String[] args) {
Commodity m1;
m1 = new Commodity();
Commodity m2 = new Commodity();
Commodity m3 = new Commodity();
Commodity m4 = new Commodity();
Commodity m5 = new Commodity();
// 给一个引用赋值,则两者的类型必须一样。m5可以给m1赋值,因为他们类型是一样的
m1 = m5;
System.out.println("m1=" + m1);
System.out.println("m2=" + m2);
System.out.println("m3=" + m3);
System.out.println("m4=" + m4);
System.out.println("m5=" + m5);
Commodity m6 = m1;
System.out.println("m6=" + m6);
m6 = m5;
System.out.println("m6=" + m6);
System.out.println("m1=" + m1);
System.out.println("m2=" + m2);
System.out.println("m3=" + m3);
System.out.println("m4=" + m4);
System.out.println("m5=" + m5);
int a = 999;
}
}
怎么更好的理解引用和实例
Commodity m1 = new Commodity();
- 使用 new 操作符可以创建某个类的一个实例。在Java程序运行的时候,所有这些创建出来的实例都被Java放在内存一个叫做堆(heap)的地方,堆可以理解为一个类似公告板的东西。
- 创建类的一个实例,就是根据类的定义,点出需要的“纸”(内存区域),加工成一个本子,挂在公告板上。实例本身,可以认为是一个小本子。
- 引用里存放的,相当于某个本子(实例)在布告板上的位置,通过这个位置我们才能定位到这本子(实例),从而访问其中的纸张(属性)。
类,对象和引用的关系
类和对象的关系
- 类相当于对象的模板,对象是类的一个实例。
- 一个 Java 程序中类名相同的类只能有一个,也就是类型不会重名。
- 一个类可以有很多对象。
- 一个对象只能根据一个类(模板)来创建。
引用和类以及对象的关系
- 引用必须是,只能是一个类的引用。
- 引用只能指向其所属的类型的类对象。
- 相同类型的引用之间可以赋值。
- 只能通过指向一个对象的引用,来操作一个对象,比如访问某个成员变量。
下面通过例程加深一下理解
public class Commodity {
String name;
String id;
int count;
double price;
}
public class Commodity1 {
String name;
String id;
int count;
double price;
}
public class ClassInstanceAndRef {
public static void main(String[] args) {
Commodity m = new Commodity();
Commodity1 m1 = new Commodity1();
Commodity commodity = m;
// 即使 Commodity 和 Commodity1 的内容一摸一样,那也是不同的类。
// 不同类的引用不可以互相赋值,因为它们本质上是不同的对象。
Commodity commodity = m1;
}
}
引用类型/对象的缺省值
null 是引用类型的缺省值,null 代表空,不存在,也常被成为空指针,因为它不指向任何已存的实例。引用类型的数组创建出来,每个元素的初始值就都是null。
null 带来的问题
Java里比较常见的错误 NullPointerException 就是因为 null 带来的问题,看一下下面这个例程,Commodity 类型的数组,因为 Commodity 是引用类型,数组创建后默认元素值是 null, 接下来我们选择性的给数组元素进行赋值,然后当做每个元素都已经被赋值 Commodity 对象一样,在循环里调用 Commodity 对象的属性,看看会发生什么问题。
public class RefAndNull {
public static void main(String[] args) {
// 数组在创建出来之后,会按照类型给数组中的每个元素赋缺省值。
// 引用类型的缺省值是null
Commodity[] ms = new Commodity[9];
// 给索引为偶数的元素赋值
for (int i = 0; i < ms.length; i++) {
if (i % 2 == 0) {
ms[i] = new Commodity();
}
}
// 依次输出数组的值
for (int i = 0; i < ms.length; i++) {
System.out.println(ms[i]);
}
for (int i = 0; i < ms.length; i++) {
Commodity m = ms[i];
System.