例如: // 声明一个类“Human” class Human{ PRivate String name; public String getName(){ return name; } public void setName(String value){ this.name = value; } //...... }
创建一个类: Human human = new Human();
其次,很多人对对象和对象的引用认识模糊 引用是程序操作对象的句柄,相当于C和C++中的指针。 前面说了,对象是一个实实在在的东西,比如前面的代码: Human human = new Human(); 程序执行到这里之后,java虚拟机将会在内存中创建一个 Human 对象,并将这个对象的引用赋给 human 变量。这里有两步,首先是创建 Human 对象,然后把创建的对象的引用赋给 human 变量。 如果声明了一个对象的引用,但没有将对象赋值给它,则这个引用指向了空的对象,或者说引用了不存在的对象。这时如果想通过这个引用访问对象,则会抛出空指针异常,例如: Human human; //...... human.setName("张三");
明白了这一点,就容易理解为什么我们可以得到抽象类的对象了:原来我们得到的抽象类的对象其实是它的已经实现了抽象方法的子类或子孙类的一个对象,但我们拿它当它的抽象类的基类来用。比如“人”这个类,每个人都会“悲伤”,男人悲伤的时候抽烟、喝酒,女人悲伤的时候哭泣、流泪。由于不同的子类在“悲伤”时所进行的动作不一样,因此这个动作(方法)在基类中不好实现,但基类中又需要有这个方法,因此,“人”这个类就可以定义一个抽象方法“悲伤”,由其子类“男人”和“女人”来实现“悲伤”这个方法。但是调用者只把男人和女人的对象当作其基类“人”的一个对象,调用它的“悲伤”方法。 读者可以去体验一下 jdk 的抽象类 java.lang.Process : Runtime runtime = Rumtime.getRuntime(); Process process = rumtime.exec("notepad.exe"); Class cls = process.getClass(); System.out.println(cls.getName()); 这时会打印出 process 类的名字,如果在 Windows 下它会是一个类似于 *Win32* 的名字,它是 Process 的一个子类。因为 process 类用于管理打开的进程,而在不同的操作系统上都有不同的实现,因此它把方法定义为 Process 的抽象方法,而具体的操作只能由对应在不同操作系统下的子实现。
下面来谈接口,我们知道接口只定义了一些方法,而没有实现这些方法。而其实,接口是一个规范,它规定了实现这个接口所要做的事情,或者说规定了实现接口的类必须具备的能力(也就是方法)。 那么我们可以这样对比: 某种类型的驾驶执照,规定了拿到这个驾照的人必须能够“开小汽车”和“开公共汽车”。那么我们认为这个驾照是一个接口,它规定了实现它的类所必须有的能力。 我们可以定义一个类 Driver,继承自 Human,然后实现“驾照持有者”这个接口: public interface DriverHolder{ public void driverCar(); public void driverBus(); } public class Driver extends Human implements DriverHolder{ public void driverCar(){ // ...... } public void driverBus(){ // ...... } }
这样一来,一个“Driver”对象,它同时也是一个 DrivreHolder 对象。即一个司机(Driver)同时是一个驾照执持有者对象。在程序中我们可以这样: DriverHolder driverholder = new Driver(); driverholder.driverCar();