在整个Java中的关键字里面this对于初学者而言是最麻烦的关键字,在程序里面,使用this可以访问本类中的属性、方法、表示出当前对象。
在具体的操作之前,首先来观察下一个小程序。
class Person { private String name ; private int age ; public Person(String n,int a) { name = n ; age = a ; }// setter、getter略 public String getInfo() { return "姓名:" + name + ",年龄:" + age ; } } public class TestDemo { public static void main(String args[]) { Person per = new Person("张三",20) ; System.out.println(per.getInfo()) ; } } |
但是以上的程序里面有一点不是很好,在构造方法上:
public Person(String n,int a) { name = n ; age = a ; } |
构造方法的主要目的是为了类之中的name、age两个属性初始化,但是构造方法里面参数的名字并不清楚。既然目的是为属性传递数据,那么最好将参数名称和属性名称保持一致。
public Person(String name,int age) { name = name ; age = age ; } |
如果按照此类的方式修改,发现name和age两个属性并没有内容。因为在构造方法里面操作的name和age和类之中的属性无关,只与构造方法的参数有关。
所有的程序都以“{}”作为边界,所有的取用都是以就近取用原则为主,如果按照此道理来讲,以上的构造方法里面所操作的name和age实际上只是构造方法里面的两个参数而已,所以现在要想明确的指定出访问的是属性则在属性访问前要加上一个“this”,使用“this.属性”的形式进行访问。
class Person { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; }// setter、getter略 public String getInfo() { return "姓名:" + this.name + ",年龄:" + this.age ; } } public class TestDemo { public static void main(String args[]) { Person per = new Person("张三",20) ; System.out.println(per.getInfo()) ; } } |
从以后所有编写的程序来讲,都必须使用this访问本类属性。
如果要强调方法,在一个类之中应该有两类方法:普通方法、构造方法。在之前也强调过,如果现在调用的是本类中的普通方法,则建议在方法前加上“this”,使用“this.方法()”调用,但是在使用this的过程之中还可以利用其实现构造方法的互调用。
构造方法是在使用关键字new实例化类对象的时候使用到的,但是利用this()可以实现构造方法间的互调用操作。
范例:观察如下代码
class Person { private String name ; private int age ; public Person() { System.out.println("1、无参构造。") ; } public Person(String name) { this() ;// 调用无参的 System.out.println("2、有一个参数构造。") ; } public Person(String name,int age) { this(name) ; // 调用有一个参数的构造 System.out.println("3、有两个参数构造。") ; } } public class TestDemo { public static void main(String args[]) { Person per = new Person("张三") ; } } |
此时的构造方法之间已经成功实现了互调用操作,但是在使用this调用构造方法的时候也有一些限制:
· 使用this调用构造方法时必须放在构造方法的首行;
· 在使用this()进行构造方法互相调用时,请至少保证留下一个出口,即:不使用this调用其它构造。
范例:观察问题代码
class Person { private String name ; private int age ; public Person() { this("hello",10) ; // 调用两个参数 System.out.println("1、无参构造。") ; } public Person(String name) { this() ;// 调用无参的 System.out.println("2、有一个参数构造。") ; } public Person(String name,int age) { this(name) ; // 调用有一个参数的构造 System.out.println("3、有两个参数构造。") ; } } public class TestDemo { public static void main(String args[]) { Person per = new Person("张三") ; } } |
此时出现了递归调用,所以从程序的编译上是无法通过的。而此特征将在日后为解释其它概念做准备。
思考题
现在要求定义一个雇员的数据类型,里面保存有编号、姓名、部门、工资,但是在这个类之中要求提供有四个构造:
· 第一个构造(无参):empno = 1000、ename = 无名氏、部门 = 未定、工资 = 0.0;
· 第二个构造(单参,传递编号):ename = 无名氏,部门 = 后勤,工资 = 1000.0;
· 第三个构造(三参,传递编号和名字、部门):工资 = 3000.0;
· 第四个构造(四个参数,全部接收)。
实现一:先按照基本思路实现
class Emp { private int empno ; private String ename ; private String dept ; private double salary ; public Emp(){ this.empno = 1000 ; this.ename = "无名氏" ; this.dept = "未定" ; this.salary = 0.0 ; } public Emp(int empno) { this.empno = empno ; this.ename = "无名氏" ; this.dept = "后勤" ; this.salary = 1000.0 ; } public Emp(int empno,String ename,String dept) { this.empno = empno ; this.ename = ename ; this.dept = dept ; this.salary = 3000.0 ; } public Emp(int empno,String ename,String dept,double salary) { this.empno = empno ; this.ename = ename ; this.dept = dept ; this.salary = salary ; } public String getInfo() { return "编号:" + this.empno + ",姓名:" + this.ename + ",部门:" + this.dept + ",工资:" + this.salary ; } } public class TestDemo { public static void main(String args[]) { System.out.println(new Emp().getInfo()) ; System.out.println(new Emp(7369).getInfo()) ; System.out.println(new Emp(7839,"KING","MANAGER").getInfo()) ; System.out.println(new Emp(7566,"ALLEN","MANAGER",2000.0).getInfo()) ; } } |
虽然以上的代码已经实现了规范的要求,但是此时有一个非常明确的错误:代码重复了。
实现二:利用this互相调用构造
class Emp { private int empno ; private String ename ; private String dept ; private double salary ; public Emp(){ this(1000,"无名氏","未定",0.0) ; } public Emp(int empno) { this(empno,"无名氏","后勤",1000.0) ; } public Emp(int empno,String ename,String dept) { this(empno,ename,dept,3000.0) ; } public Emp(int empno,String ename,String dept,double salary) { this.empno = empno ; this.ename = ename ; this.dept = dept ; this.salary = salary ; } public String getInfo() { return "编号:" + this.empno + ",姓名:" + this.ename + ",部门:" + this.dept + ",工资:" + this.salary ; } } public class TestDemo { public static void main(String args[]) { System.out.println(new Emp().getInfo()) ; System.out.println(new Emp(7369).getInfo()) ; System.out.println(new Emp(7839,"KING","MANAGER").getInfo()) ; System.out.println(new Emp(7566,"ALLEN","MANAGER",2000.0).getInfo()) ; } } |
此时的代码完成了与之前同样的功能,而且更加的简单。
任何时候代码好与坏,最简单的判断的标准:看能否以最短的长度实现最多的功能。
所谓的当前对象严格来讲指的就是当前调用本类方法的对象。
范例:观察当前对象
class Demo { public void print() { System.out.println("** Demo类中的输出:" + this) ; } } public class TestDemo { public static void main(String args[]) { Demo da = new Demo() ; System.out.println("主类中的输出:" + da) ; da.print() ; System.out.println("====================") ; Demo db = new Demo() ; System.out.println("主类中的输出:" + db) ; db.print() ; } } |
那么在之前所谓的“this.属性”严格来讲就是取得当前对象中的属性。
范例:来看一个没事找抽的题目
class Demo { private MyUtil mu ; public Demo() { // 2、执行构造方法 this.mu = new MyUtil(this) ; // 3、实例化MyUtil类对象,this相当于demo this.mu.fun() ;// 6、调用MyUtil方法 } public void print() { // 8、执行此方法 System.out.println("Hello World .") ; } } class MyUtil { private Demo demo ; public MyUtil(Demo d) { // 4、调用构造,d = 主类的demo this.demo = d ;// 5、将主类的demo传递给MyUtil类的demo属性 } public void fun() { this.demo.print() ; // 7、调用Demo类的方法 } } public class TestDemo { public static void main(String args[]) { Demo demo = new Demo() ; // 1、实例化Demo类对象 } } |
如果觉得以上代码过于别扭,就可以pass了。
2.1、引用传递是整个Java之中的基础的灵魂部分,在日后的所有开发都会有所涉及,那么为了更好的帮助大家理解引用传递的过程,将通过三个程序做一个分析。
范例:第一道程序
class Demo { private int num ; public Demo(int num) { this.num = num ; } public void setNum(int num) { this.num = num ; } public int getNum() { return this.num ; } } public class TestDemo { public static void main(String args[]) { Demo demo = new Demo(10) ; fun(demo) ; System.out.println(demo.getNum()) ; } public static void fun(Demo temp) { temp.setNum(30) ; } } |
本程序是一个最基础的引用传递的操作实现,在方法之中只需要接收Demo类对象,就表示两个栈内存指向同一块堆内存空间,所做的修改一定可以保留下来。
范例:第二道引用传递范例
public class TestDemo { public static void main(String args[]) { String str = "Hello" ; fun(str) ; System.out.println(str) ; } public static void fun(String temp) { temp = "World" ; } } |
本题目解释只有一句话:“字符串的内容一旦声明则不可改变”。那么在进行画图描述之前,首先再来观察如下代码。
public class TestDemo { public static void main(String args[]) { int x = 10 ; fun(x) ; System.out.println(x) ; } public static void fun(int temp) { temp = 20 ; } } |
本程序属于一个数值关系,传递到方法里面的数据就相当于是一个数据的拷贝,至于方法里面如何修改与之前无关。
对于String这种数据类型由于其本身的特性所决定的操作模式(不可改变的模式),那么如果觉得堆栈关系过于难以理解,就按照基本类型的方式理解。
范例:第三道引用范例
class Demo { private String msg ; public Demo(String msg){ this.msg = msg ; } public void setMsg(String msg) { this.msg = msg ; } public String getMsg() { return this.msg ; } } public class TestDemo { public static void main(String args[]) { Demo demo = new Demo("Hello") ; fun(demo) ; System.out.println(demo.getMsg()) ; } public static void fun(Demo temp) { temp.setMsg("World") ; } } |
这道范例严格来讲和第一道程序的分析是一样的,所以下面根据两种情况进行分析,一个是将String当成普通类型的方式理解,另外是使用完整的关系来分析。
以上的分析方式严格来讲并不标准,因为String毕竟是一个引用数据类型,那么引用数据类型需要有堆、栈关系。
简单Java类可以描述出一类事物,那么现在要求通过面向对象的基本概念描述一个现实生活:一个人有一辆汽车,一辆车只属于一个人。假设人之中只包含有姓名和年龄、汽车只包含有品牌。
换个思路:如果说现在完成的不是程序,而是数据表的建立,那么请问该如何设计数据表呢?
DROP TABLE person PURGE ; DROP TABLE car PURGE ; CREATE TABLE person( pid NUMBER , name VARCHAR2(20) , age NUMBER(3) , CONSTRAINT pk_pid PRIMARY KEY(pid) ) ; CREATE TABLE car( cid NUMBER , name VARCHAR2(20) , pid NUMBER , CONSTRAINT pk_cid PRIMARY KEY(cid) , CONSTRAINT fk_pid FOREIGN KEY(pid) REFERENCES person(pid) , CONSTRAINT uk_pid UNIQUE(pid) ) ; |
实际上现在就可以发现一些有意思的事情,如果要定义类,那么也需要定义两个类,而且类之中的属性也和表类似。所以现在就可以得出这样的一个结论:“在实际的工作之中,简单Java类的设计完全由表的结构而衍生”,存在有如下的对应关系:
· 类名称 = 实体表的表名称;
· 类属性 = 表中的基本字段;
· 引用关系 = 外键;
· 一个实例化对象 = 表的一行记录;
· 对象数组 = 表的多行记录。
那么下面就可以根据以上分析的数据库创建脚本来进行类的创建,但是在编写的时候请注意一个过程:先编写基本的组成字段,而后再编写关联字段。
范例:基本实现
class Person { private int pid ; private String name ; private int age ; // 一个人有一辆车,如果car为空表示没车 private Car car ; public Person() {} public Person(int pid,String name,int age) { this.pid = pid ; this.name = name ; this.age = age ; } public void setCar(Car car) { this.car = car ; } public Car getCar() { return this.car ; } public String getPersonInfo() { return "人员编号:" + this.pid + ",姓名:" + this.name + ",年龄:" + this.age ; } } class Car { private int cid ; private String name ; private Person person ; public Car() {} public Car(int cid,String name) { this.cid = cid ; this.name = name ; } public void setPerson(Person person) { this.person = person ; } public Person getPerson() { return this.person ; } public String getCarInfo() { return "车编号:" + this.cid + ",品牌:" + this.name ; } } public class TestDemo { public static void main(String args[]) { // 第一层次:设置关系 Person per = new Person(10,"刁雪冰",30) ; Car c = new Car(10001,"玛莎拉帝") ; per.setCar(c) ; // 一个人有一辆车 c.setPerson(per) ; // 一辆车属于一个人 // 第二层次:根据关系取数据 System.out.println(per.getPersonInfo()) ; System.out.println(per.getCar().getCarInfo()) ; // 代码链 System.out.println(c.getPerson().getPersonInfo()) ; } } |
一个人有一个孩子,孩子也有一辆车。
class Person { private int pid ; private String name ; private int age ; // 孩子的信息就属于人的信息 private Person child ; // 一个人有一辆车,如果car为空表示没车 private Car car ; public Person() {} public Person(int pid,String name,int age) { this.pid = pid ; this.name = name ; this.age = age ; } public void setChild(Person child) { this.child = child ; } public Person getChild() { return this.child ; } public void setCar(Car car) { this.car = car ; } public Car getCar() { return this.car ; } public String getPersonInfo() { return "人员编号:" + this.pid + ",姓名:" + this.name + ",年龄:" + this.age ; } } class Car { private int cid ; private String name ; private Person person ; public Car() {} public Car(int cid,String name) { this.cid = cid ; this.name = name ; } public void setPerson(Person person) { this.person = person ; } public Person getPerson() { return this.person ; } public String getCarInfo() { return "车编号:" + this.cid + ",品牌:" + this.name ; } } public class TestDemo { public static void main(String args[]) { // 第一层次:设置关系 Person per = new Person(10,"刁雪冰",30) ; Person chd = new Person(20,"刁三",10) ; Car c = new Car(10001,"玛莎拉帝") ; Car cc = new Car(10002,"别摸我") ; per.setCar(c) ; // 一个人有一辆车 c.setPerson(per) ; // 一辆车属于一个人 per.setChild(chd) ; // 一个人有一个孩子 chd.setCar(cc) ; // 孩子有车 cc.setPerson(chd) ; // 车属于孩子 // 第二层次:根据关系取数据 System.out.println(per.getCar().getCarInfo()) ; // 代码链 System.out.println(c.getPerson().getPersonInfo()) ; System.out.println(per.getChild().getPersonInfo()) ; System.out.println(per.getChild().getCar().getCarInfo()) ; } } |
那么在实际的生活之中也到处充满这此概念,例如:一个人有一张嘴、多个牙齿。
class 人 { private 嘴 对象 ; private 牙齿 [] 对象 ; } class 嘴 {} class 牙齿 {} |
思考:电脑有主机、显示器、鼠标、键盘所组成,还有CPU、主板、声卡、网卡、内存条、硬盘组成。
class 主机 { private CPU [] 对象 ; private 声卡 [] 对象 ; private 网卡 [] 对象 ; private 内存条 [] 对象 ; private 硬盘 [] 对象 ; } class 显示器 {} class 鼠标 {} class CPU {} class 声卡 {} class 网卡 {} class 内存条 {} class 硬盘 {} class 主板 {} class 键盘 {} class 电脑 { private 显示器 对象 ; private 主机 对象 ; private 鼠标 对象 ; private 键盘 对象 ; } |
现在要求抽象出当前教室。
class 门 {} class 窗 {} class 黑板 {} class 空调 {} class 桌子 {} class 椅子 {} class 学生 {} class 老师 {} class 教室 { private 门 [] 对象 ; private 窗 [] 对象 ; private 黑板 对象 ; private 空调 对象 ; private 桌子 [] 对象 ; private 椅子 [] 对象 ; private 学生 [] 对象 ; private 老师 对象 ; } |
这些都属于引用数据类型的应用,如果更加严格的用概念描述,这些属于合成设计模式。