面向对象并不是JAVA中所独有的,而是一种编程思想,这种编程思想在很多编程语言中都使用到了,如C/C++、Python等,而JAVA自然也引入了面向对象的概念。
面向对象程序设计方法中很重要的三个特性就是:
- 封装:封装有两层含义:
- 一是指把对象的属性和行为包装为一个整体
- 二是将属性和行为与外界进行隔离,可以只暴露给外界有限的信息,而隐藏内部实现细节
- 继承:继承主要是为了增强代码的复用性,提高软件的开发效率
- 多态:JAVA和C/C++类似,也存在两种多态形式:
- 方法重载
- 对象多态:子类对象可以与父类对象进行相互转换,而根据子类不同其对应可完成功能也不同
JAVA中类与对象的定义与C/C++并没有什么不同,只是C/C++称类中的函数为成员函数和成员属性,而JAVA中则称之为属性和方法。
JAVA中类的定义语法为:
class classname {
datatype prop;
...
public returntype functype(argtype arg,...) {
// statement;
[return returntype;]
}
}
类中的属性在开发中不一定只是变量,也有可能是其它内容,所以也会称之为成员(field)。
如果定义了下边的这样一个类:
class Book {
String name;
double price;
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
}
并在main中这样调用:
public class Demo {
public static void main(String args[]) {
Book bk = new Book();
bk.name = "English";
bk.price = 12.5;
bk.getInfo();
}
}
产生的结果为:
Book name is English,book price is 12.5
从这样看来,JAVA和C/C++的语法结构上还是很相似的。
实例化对象之前提到,类属于引用数据类型,而引用数据类型会涉及到内存的分配和引用。对象的产生要通过声明并进行实例化:
classname var = new classname();
或者:
classname var = null;
var = new classname();
上面实例化一个对象用到了new关键字,在C/C++中,new是用来申请内存的。JAVA中同样也可以用来为实例化对象申请内存。而其余类方法的调用与C/C++的使用方法类似。
同样之前在C/C++中也提到,一般变量是放在栈内存当中,一旦声明,统一销毁,而使用new申请的内存则是存放在堆内存当中,随用随开,用完自销。
而JAVA中也有类似的概念:
- 堆内存:保存每一个对象的属性内容,需要使用new关键字才能够开辟,如果某个对象没有对应的堆内存指向,将无法使用
- 栈内存:保存的是一块堆内存的地址数据,所以每一块堆内存只能够保留一块堆内存地址
对于类对象来说,栈内存保存的就是bk这样的引用变量名,而堆内存保存的则是bk实际指向的对象,包含name,price等属性。
而若是只用Book bk = null;声明了对象,但却并没有为其实例化,则会在引用对象属性或调用对象方法的时候出错,因为此时并没有为该应用数据类型开辟堆内存空间。
封装但是实际情况中,上面的Book并不会这样定义,而是换一种方式:
class Book {
private String name;
private double price;
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(double var) {
price = var;
}
public double getPrice() {
return price;
}
}
在这里,JAVA与C/C++的封装形式开始出现了一些区别,C/C++中使用public:/private:来分割不同访问类型的属性或者方法,而JAVA这里则是将访问类型放在了属性或方法定义的最前方。
也因此,使用private对属性进行封装,而使用public对外界开放方法接口,才形成了隔离和实现隐藏,这也就是封装的含义。同时,为了能够自定义属性内容,可以自定义的属性最好要设定setter和getter方法。
而此时,该类的使用和方法调用就变为了:
public class Demo {
public static void main(String args[]) {
Book bk = new Book();
bk.setName("English");
bk.setPrice(12.5);
bk.getInfo();
}
}
构造
上边的形式虽然可以自定义属性内容,但是C/C++中利用构造函数可以在实例化对象的同时就对属性进行赋值,JAVA当然也存在这样的功能:
class Book {
private String name;
private double price;
public Book() {
System.out.println("This is Book's constructer.");
}
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(double var) {
price = var;
}
public double getPrice() {
return price;
}
}
C/C++中的构造函数是当实例化对象时发生调用,方法名称与类名相同,没有返回值,可以进行重载。而JAVA中的构造方法基本也是一样的用法,在上面的代码段中添加了两个构造方法。对应的,此时的main方法就可以修改为:
public class Demo {
public static void main(String args[]) {
Book bk = new Book();
bk.getInfo();
Book bk2 = new Book("English",12.5);
bk2.getInfo();
}
}
执行结果为:
This is Book's constructer.
Book name is null,book price is 0.0
Book name is English,book price is 12.5
从上面的例子可以看出,和C/C++一样,JAVA中也存在一个默认无参的构造方法。这也就是实例化类对象要用Book()而不是Book的缘故。
构造方法一般是用于对象的初始化,而若是存在自定义的构造方法,便会覆盖原有的默认无参构造方法,因此为了Book()实例化对象不至于报错,同时应该存在自定的无参构造方法。
而属性也可以存在默认值,这点在log打印或者表达文件路径中经常用到,这里先说明一下用法:
class Book {
private int VERSION = 1;
private String name;
private double price;
public Book() {
System.out.println("This is Book's constructer, version is " + VERSION);
}
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price + ",version is " + VERSION);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(double var) {
price = var;
}
public double getPrice() {
return price;
}
}
如上面的代码所示,添加了一个默认属性VERSION,因为直接给了默认值,这里将该属性写为了大写,同时没有给出setter方法。此时进行实例化:
public class Demo {
public static void main(String args[]) {
Book bk = new Book();
bk.getInfo();
}
}
结果为:
This is Book's constructer, version is 1
Book name is null,book price is 0.0,version is 1
对象实例化的过程,一般要经历类的加载,内存的分配,默认值的设置,构造方法。而构造方法属于整个对象构造过程的最后一步,这一步是留给用户自己处理的步骤。
而在上面的结果显示中,VERSION这个变量却在自定义的构造方法调用之前就完成了赋值,所以对于用户来说,其VERSION属性就是默认值。
匿名对象匿名对象从名称来说就是没有名字的对象。之前提到栈内存中保存着对象的变量名,而其实际的属性和方法则保存在堆内存中,由栈内存的变量名指向该堆内存,完成引用和访问。
而匿名对象很显然就是省略了变量名,换句话说也就是没有在栈内存中进行声明,而却仍能够指向某块堆内存。考虑下面的代码段:
public class Demo {
public static void main(String args[]) {
new Book().getInfo();
}
}
结果为:
This is Book's constructer, version is 1
Book name is null,book price is 0.0,version is 1
在上面的代码中,并没有变量指向Book对象,但是仍然开辟了堆内存,完成了方法调用。而由于这种方法创建的匿名对象并没有对应的栈内存指向,所以只能使用一次,之后由于没有栈内存明确地指向该堆内存,就会被GC(Garbage Collector)回收释放。
this同时上面的代码中还用到了另一个关键字this,和C/C++一样,this指向了当前的对象,以表示和传入参数进行区分。
上面this只是调用的本类中的属性,除此之外还可以调用本类方法:
- 调用本类普通方法:可以使用 this.func() 调用
- 调用本类构造方法:在一个构造中要调用其它构造,可以使用 this() 调用
将上边的代码修改为:
class Book {
private String name;
private double price;
public Book() {
System.out.println("This is Book's constructer.");
}
public Book(String name, double price) {
this();
this.name = name;
this.price = price;
}
public void getInfo() {
System.out.println("Book name is " + name + ",book price is " + price);
}
public void setName(String var) {
name = var;
}
public String getName() {
return name;
}
public void setPrice(double var) {
price = var;
}
public double getPrice() {
return price;
}
}
然后:
public class Demo {
public static void main(String args[]) {
Book bk = new Book("English",12.5);
bk.getInfo();
}
}
此时的结果为:
This is Book's constructer.
Book name is English,book price is 12.5
也就实现了this对构造方法的调用。但同时使用this调用构造方法时,也会存在两个限制:
- 使用 this() 调用构造方法形式的代码只能够放在构造方法的首行
- 进行构造方法互相调用时,要保留调用的出口
透过上面的过程,也差不多能够搞清楚JAVA类的使用和定义方法了。这里再说明下简单JAVA类的基本开发要求:
- 类名称必须存在意义
- 类中所有的属性必须用private进行封装
- 类中可以提供任意多个构造方法,但必须保留一个无参构造方法
- 类中不允许出现任何输出语句,所有信息输出必须交给被调用处输出
- 类中需要提供有一个取得对象完整信息的方法,而且返回String型数据
上面的要求结合之前的说明和理解便可以明白,这里不赘述。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)