当前位置:   article > 正文

面向对象_老师事物:设置特性和功能 特征:姓名、年龄、住址 功能:讲课 测试: 创建teacher对

老师事物:设置特性和功能 特征:姓名、年龄、住址 功能:讲课 测试: 创建teacher对

第5章 Java面向对象程序设计
在日常的工作与生活中,我们对于事物的认知会按照分类的方式来,比如说在商场中我们会将各种商品进行分类,划分为食品区、洗漱区、酒水区、玩具区等等,之所以这样划分的原因是因为这些事物存在一定的共同的特点,将具有相同或者相似特点的事物集中在一起便于管理以及操作。那对于编程来说,其面对的是工作与生活中的实际问题,我们需要将生活与工作中问题的各个方面与程序的各个部分进行对应,对于Java编程来说就很好的做到了这一点,Java语言根据不同事物的特征以及功能进行划分,这样的一类事物称为一个类,一类事物中的某个个体我们称为对象。Java在编程的时候将实际问题中的各个对象表示出来,针对其进行编程,完成问题的解决。
5.1面向对象基础
5.1.1面向对象编程思想
说到面向对象,我们不得不提的就是编程经常使用的另外一种编程思想,也就是面向过程的编程思想,这种编程思想出现较早,在编程的过程中如果使用面向过程的编程思路的时候,我们需要怎么处理呢?所谓的过程就是处理一个问题的先后过程,这个时候作为程序的编程人员需要考虑的问题就是在处理相关问题的时候的相关顺序,第一步完成哪些操作,第二步完成哪些操作,第三步完成哪些操作,一直到整个问题得到解决,其中的每一个步骤可以使用自定义方法的方式来完成,整个过程中需要使用到多个方法,并且最后需要将这些方法按照一定的先后顺序串联在一起,这个时候就需要编程人员对于整个问题的处理过程十分熟悉,不然在一些细节的地方就可能出现错误。
相对于面向过程来说,面向对象的思想又是怎么样的呢?先举一个例子,比如现在你完成高中学习之后,经过高考,取得了一个很好的成绩,你想报考一所更适合自己的大学,但是不同大学的开设的课程是不一样的,各有所长,那这个时候你自然而然想到去各大学校的官网上去了解一下每所学校的课程设置、师资力量、基础设施等等。这个时候其实就是在做一件面向对象的事,面对于报考学校这个问题选择的处理方式是通过学校官网这个角色来完成任务的,而不是自己每所学校都去调查一番,然后再进行选择,因为这样的话时间和消耗都比较大,对于各个大学来说都提供了比较权威的信息在官网上,可供学生参考。在面向对象编程的思想下,人们关注的不在是每个方法,而是关注的不同的身份角色,这些身份角色也就是我们说的Java类,就如同我们对于超市的商品分类而言,我们通过分类来区分不同的商品,这里我们通过Java类来区分程序处理过程中的各个角色身份以及各个环节,编程人员从一开始关注问题的每一步怎么处理,转而关注角色的划分,让不同角色也就是Java类负责不同的功能,这样一来编程的过程中更加利于任务的拆解,任务的分工,在自己不熟悉的领域可以由更擅长的人来完成。在编程的时候用不用向面向过程似得对于整个过程都要熟悉,面向对象思想下编程者主要关注于自己负责的环节即可。
我们可以使用一些例子来对比一下面向过程编程思想与面向对象编程思想的差异,比如经常有人说的一个玩笑:将大象装进冰箱分为几步呀?对于将大象装进冰箱来说,我们可以从上面的两种思想来进行讨论。首先如果基于面向过程的思想来完成这个操作,主要操作来说会划分为三步,人将冰箱门打开、人将大象放进去、人将冰箱门关上。从这样的语言描述中我们可以看到这完全是一个在由人控制的一个过程化操作,人参与其中是一个参与者的身份,离开了人这个过程是无法运转的。这个时候我们再看一下面向对象编程思想下这个问题是怎么解决的,从人的认知角度来说,冰箱本身是具有开门以及关门的能力的,大象具有行走能力的,人不需要去费力操作他们,所以是这样的,冰箱开门、大象进入冰箱、冰箱关门。在这个过程中人只需要作为一个控制者即可,不要自己动手操作。从前后两种表述中我们可以看到,对于面向过程的编程思想对于人的依赖是很大的,而面向对象的编程思想可以很好的将人从这个过程中解放出来,对于具体的操作由冰箱以及大象来完成,这样人就可以将精力放到更复杂问题的处理上,无需在某个环节上耗费太多时间。这也是我们现在生活与工作中使用最多的逻辑,比如,你想更快速的出行,直接的想法是去4S店买一辆汽车,而不是自己设计生产一辆汽车,这样会耗费很多精力在造车环节上,其他事情的处理自然精力不足。在进行开发中同样的道理,面向过程的编程语言要求一组开发人员对某个需要开发的系统的各个环节都非常了解,这样难度比较大,尤其是遇到瓶颈性问题时,然而面向对象的编程语言可以解决这个问题,它可以使某个项目组仅仅负责自己最擅长的一部分开发,对于自己能力欠缺的部分可以由其他团体负责完成,公司通过购买具有一定功能的对象即可,可以实现分工开发,高效组装。
对于Java语言来说,它是一门完全面向对象的编程语言。在Java编程的过程中我们使用Java类作为我们编程的一个基本单位,通过类与类之间的彼此使用来完成功能,就如同前面的示例中我们需要从键盘录入一下信息,我们直接使用了一个工具: Scanner。由于其具有很好的录入功能,我们可以很轻松的完成录入,从而可以将更多的精力放到程序的逻辑编写上,更好的完成业务操作。如同Scanner类一样Java为我们提供了很多方便的功能帮助我们更好的完成编程,比如:I/O操作、网络操作、线程操作、集合操作、加密操作等。有了这下工具,我们就可以根据不同的业务逻辑完成程序的编写。
5.1.2类的定义
Java类作为我们Java编程的一个基本单位,同时也是面向对象编程思想的一个体现。在实际的编程过程中,我们需要根据需要处理的实际问题编写Java类。基于面向对象的编程思想我们需要从需要解决的问题中划分出不同的Java类,我们延续上一节的例子,将大象放进冰箱,在这个过程中我们可以看到其中主要有人、冰箱、大象这三个身份,对于处理过程中的开门、关门、进入冰箱,这些操作来说,我们需要探讨一下他们隶属于哪个身份,首先我们讨论这些操作是否属于人所有,可能很多人认为这些操作都是属于人的,但是可以考虑一下,开门、关门只是由人控制的,他们真正隶属的是冰箱,冰箱具有开门与关门的功能,人控制冰箱完成功能而已,同理进入冰箱的功能是属于大象的,人也只是控制而已。这样我们就可以从刚才的问题中划分出两个Java类,一个类代表冰箱具有两个功能,一个类代表大象具有进入冰箱的功能。人控制冰箱与大象来完成最终的任务。
对于一个Java类来说除了包含具有的功能之外还需要表示一些基础信息,比如冰箱的品牌、容量、生产厂家、价格等等。我们可以这样总结Java类是针对具有相同属性的一系列事物的个体进行的抽象,从而得到的整体描述,其中既包括事物所有具有的属性特征,同时也包括事物所有具有的行为功能,这两部分作为一个类的两大组成部分,在Java代码中分别使用变量来表示属性特征,使用方法来表示行为功能。
Java类的定义需要符合一定的格式,其中包括类的声明以及类的主体,在主体中包含变量与方法,我们一般称为成员变量、成员方法。其格式为:
类的声明
{
成员变量
成员方法
}
我们将上的示例中与冰箱相关的Java类写出来,代码如下:
/**

这里是代表冰箱的Java类
  • 1

*/
public class IceBox
{
//品牌名称
public String brand;

//生产厂家
public String factory;

//容量
public String volume;

//价格
public double price;

//开门
public void openDoor()
{
	System.out.println( "开门" );
}

//关门
public void closeDoor()
{
	System.out.println( "关门" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

}
从上面的代码中我们可以看到,对于表示冰箱的类包含了多个属性特征并且使用成员变量来表示,也包含了多个行为功能使用成员方法来表示的。在编程过程中我们需要针对问题的具体情况划分出不同的Java类并编写出来。
对于Java类来说除了成员变量与成员方法这两个部分之外,我们还需要一些其他内容,这包括构造方法、set/get方法、toString方法等。下面我们分别解释一下这几个部分在Java类中所起的作用。
构造方法,对于Java类来说,如果我们需要使用Java类来编程就需要先对其进行创建实例的操作,这个时候就需要使用到构造方法。构造方法的作用主要是用来完成Java类的实例创建操作,以及在实例创建过程中给实例的各个成员变量进行赋值操作。对于构造方法来说需要需要遵循一定的格式,格式如下:
修饰符 构造方法名称(形式参数列表)
{
构造方法的方法体
}
对于修饰符我们选择使用public,构造方法的名称需要与类名相同,对于形式参数列表来说由于构造方法的作用是给成员变量进行赋值,一般形式参数列表从成员变量中进行选取,方法体中主要是对于成员变量的赋值操作。对于构造方法来说,一个Java类中可以有多个构造方法其中可分为无参构造方法、有参构造方法。其中无参构造方法指的是形式参数列表不书写任何内容,方法体也不需要进行操作。有参构造方法指的是形式参数列表从成员变量中根据需要进行选择一到多个,方法体中执行对成员变量的赋值操作。我们将上面的冰箱类,添加上构造方法,代码如下:
/**

这里是代表冰箱的Java类
  • 1

*/
public class IceBox
{
//品牌名称
public String brand;

//生产厂家
public String factory;

//容量
public String volume;

//价格
public double price;

//无参构造方法
public IceBox()
{
}

//有参构造方法
public IceBox(String brand,String factory,String volume)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
}

//有参构造方法
public IceBox(String brand,String factory,String volume,double price)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
	this.price = price;
}

//开门
public void openDoor()
{
	System.out.println( "开门" );
}

//关门
public void closeDoor()
{
	System.out.println( "关门" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

}
从中我们可以看到,添加了三个构造方法,分别是无参构造方法、三个参数的构造方法、四个参数的构造方法,不同的构造方法可以根据不同的需要进行使用,完成创建类的实例以及对不同成员变量的赋值操作。其中我们也可以看到这些构造方法之间构成了方法的重载关系。这里还需要注意一个问题,就是对于无参构造方法,在编写Java类的时候如果没有写出来,JDK会自动提供,但是这里建立大家编写Java类时,直接手动给出无参构造,因为如果不给出的时候如果写了另外一个有参构造方法,会将无参构造方法覆盖掉,无参构造方法在很多时候需要使用。
set/get方法,在Java类中,我们除了使用构造方法给成员变量进行赋值操作以外,我们还可以提供其他的方法来完成对成员变量的操作,set系列的方法可以用来对成员变量进行赋值操作,get系列的方法可以用来获取成员变量的值。对于成员变量来说,当使用public修饰的时候,可以在另外一个类中通过类的实例直接使用,这个时候回存在一些风险,比如:对于冰箱类其中的价格price,我们可以给其赋值为一个负数,这显然是不合适的,因为商品的价格肯定是一个正数,不会出现负数的情况,那这个时候最后可以通过代码进行限制,当需要进行赋值的数值是正数时,才给对应的成员变量进行赋值,但是如果只是成员变量的话是不能完成限制操作的,需要使用到自定义的方法来完成这个操作,这个时候set系列的方法就有用了。对于Java类中的成员变量我们都可以加上set系列以及ge系列的方法来辅助对成员变量的操作。我们给冰箱类加上set/get方法,代码如下:
/**

这里是代表冰箱的Java类

*/
public class IceBox
{
//品牌名称
public String brand;

//生产厂家
public String factory;

//容量
public String volume;

//价格
public double price;

//无参构造方法
public IceBox()
{
}

//有参构造方法
public IceBox(String brand,String factory,String volume)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
}

//有参构造方法
public IceBox(String brand,String factory,String volume,double price)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
	this.price = price;
}

//完成对成员变量品牌的设置
public void setBrand(String brand) {
	
	this.brand = brand;
}

//获取成员变量品牌的值	
public String getBrand() {
	return brand;
}

//完成对成员变量价格的设置
public void setPrice(double price) {
	
	//当价格大于0时,才进行赋值操作
	if(price > 0)
	{
		this.price = price;
	}
	else
	{
		this.price = 0.0;
	}
}

public double getPrice() {
	return price;
}
//开门
public void openDoor()
{
	System.out.println( "开门" );
}

//关门
public void closeDoor()
{
	System.out.println( "关门" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

}
在上面的代码中添加了对brand、price两个成员变量的set/get方法,对于其他成员变量也可以按照这种方式来操作。
toString方法,在编程时,我们经常遇到需要查看某个类的具体事例的所有成员变量的信息这个操作,虽然上面的提供了get方法可以用来获取对应成员变量的值,但是需要操作多次,比较麻烦,Java为我们提供了一个统一的方法,用于将一个实例中的所有成员信息一次性转换为字符串,从而方便进行查看。在冰箱类中加入toString方法,代码如下:
/**

这里是代表冰箱的Java类

*/
public class IceBox
{
//品牌名称
public String brand;

//生产厂家
public String factory;

//容量
public String volume;

//价格
public double price;

//无参构造方法
public IceBox()
{
}

//有参构造方法
public IceBox(String brand,String factory,String volume)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
}

//有参构造方法
public IceBox(String brand,String factory,String volume,double price)
{
	this.brand = brand;
	this.factory = factory;
	this.volume = volume;
	this.price = price;
}

//完成对成员变量品牌的设置
public void setBrand(String brand) {
	
	this.brand = brand;
}

//获取成员变量品牌的值	
public String getBrand() {
	return brand;
}

//完成对成员变量价格的设置
public void setPrice(double price) {
	
	//当价格大于0时,才进行赋值操作
	if(price > 0)
	{
		this.price = price;
	}
	else
	{
		this.price = 0.0;
	}
}

public double getPrice() {
	return price;
}


@Override
public String toString() {
	return "IceBox [brand=" + brand + ", factory=" + factory + ", volume=" + volume + ", price=" + price + "]";
}

//开门
public void openDoor()
{
	System.out.println( "开门" );
}

//关门
public void closeDoor()
{
	System.out.println( "关门" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

}
我们可以看到在上面的代码中加入了toString方法,其方法体内将所有的成员变量进行了字符串的拼接操作,从而将实例的所有成员变量信息集合在一起。
在编写Java类时,我们需要根据具体要处理的问题,从中分析出包含的Java类,每个Java类都具有一定的属性特征一级行为功能。对于Java的类的定义中,我们一般需要包含以下几个部分:成员变量、成员方法、构造方法、set/get方法、toString方法等。
5.1.3 对象的创建和使用
在通过编程去解决实际问题的过程中,我们需要对具体问题进行细致的描述,通过面向对象的思想将问题中具有相同属性特征以及行为功能的一系列事物抽取出来定义成为Java类,在Java类中通过成员变量表示事物的属性特征,比如一个人的姓名、年龄、性别、身份证号、家庭住址等,通过成员方法标识事物的行为功能,比如学生具有的学习能力、老师具有的教学能力、司机具有的驾驶能力等。无论属性特征还是行为功能是这一类事物共同具有的,当我们进行编程操作的时候,我们不可能利用一类事物处理某些操作,而是这一类事物中的某个个体,比如说,学生进行考试,可以执行考试这个操作的是具体的一个学生,老师记进行讲课,也是具体的某个老师讲课。这个时候我们可以看到Java类是一个整体概念,具体进行执行某项操作的是具体的单个个体,这个个体我们称为实例或者对象。
在编程过程中我们需要使用到对象,对象需要通过具体的Java类创建而来,在通过分析具体的问题处理逻辑之后我们将其中的相关部分抽取称为Java类,可以知道作为Java类的组成包括:成员变量、成员方法、构造方法、set/get方法、toString方法,在这其中的构造方法具有的作用就是创建类的对象,并且在创建的过程中,给对象的成员变量进行赋值操作。对于对象的创建操作我们需要按照一定的格式来完成,其格式为:
类名称 对象名 = new 构造方法名称(实际参数);
比如我们定义了一个Java类Student,其中包含姓名、年龄、性别的成员变量以及有参构造方法用来对这三个成员变量赋值,那么我们可以通过代码:Student stu = new Student(“张三”,10,”男”); 这行代码完成的就是创建了一个Sudent类的对象,并且将对象的成员变量分别进行了赋值。对于对象的创建我们可以通过代码来掩饰,首先我么可以定义两个Java类分别为Student、Teacher,在Test类中分别创建两个类的对象,代码如下:
/**
这里定义一个Java类用来表示学生

*/
public class Student
{
//学号
public String studentId;

//姓名
public String stuName;

//年龄
public int stuAge;

//性别
public String stuSex;

//无参构造方法
public Student()
{
}

//有参构造方法
public Student(String studentId,String stuName,int stuAge,String stuSex)
{
	this.studentId = studentId;
	this.stuName = stuName;
	this.stuAge = stuAge;
	this.stuSex = stuSex;
}

//学生具有的学习能力
public void study()
{
	System.out.println( "学习使人进步" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

}
/**
当前这个类表示的是老师

*/
public class Teacher
{
//教师编号
public String teacherId;

//姓名
public String teacherName;

//年龄
public int teacherAge;

//性别
public String teacherSex;

//无参构造方法
public Teacher()
{
}

//有参构造方法
public Teacher(String teacherId,String teacherName,int teacherAge,String teacherSex)
{
	this.teacherId = teacherId;
	this.teacherName = teacherName;
	this.teacherAge = teacherAge;
	this.teacherSex = teacherSex;
}

//老师具有的讲课能力
public void teach()
{
	System.out.println( "讲授知识" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

}
/**
当前这个类主要作为程序的入口,在当前类中使用
已经编写的其他Java类创建对象
*/
public class Test
{
//程序的入口
public static void main(String[] args)
{

	//创建Student类的对象
	
	//方式一:通过无参构造创建对象
	Student stu1 = new Student();

	stu1.studentId = "s001";
	stu1.stuName = "张三";
	stu1.stuAge = 20;
	stu1.stuSex = "男";

	//方式二:通过有参构造创建对象
	Student stu2 = new Student( "s002", "李四", 22, "女" );


	//创建Teacher类的对象

	//方式一:通过无参构造创建对象
	Teacher teacher1 = new Teacher();

	teacher1.teacherId = "t001";
	teacher1.teacherName = "王五";
	teacher1.teacherAge = 30;
	teacher1.teacherSex = "男";

	//方式二:通过有参构造创建对象
	Teacher teacher2 = new Teacher( "t002", "赵六", 29, "女" );


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

}
通过上面的代码我们可以看到,在对于自定义的Java类Student、Teacher,在Test类中main方法里对其进行了对象的创建操作。对象的创建需要通过构造方法来完成。
当通过对需要处理的问题的分析之后定义出Java类,并在其他类中完成了该类的对象的创建操作,接下来我们需要对这个对象进行使用,对于一个Java类来说主要包含了成员变量与成员方法这两大部分,当类创建了对象之后,每个对象都具有相关的成员变量与成员方法,我们对于对象的使用其实是对成员变量与成员方法的使用,在上面的代码中我们可以看到当我们使用无参构造创建对象后,需要给对象中的成员变量进行赋值操作,我们可以使用:对象名.成员变量名 = 值; 这个格式对成员变量进行使用并且完成赋值操作。除此之外我们还要使用对象中的成员方法,遵循的格式为:对象名.成员方法名称(实际参数列表); 对于方法存在返回值的需要定义变量来接收返回值。
对于上面的成员变量的使用存在一个缺陷,比如当我们使用stuAge这个成员变量的时候,我们可以给其赋值一个非法值,stu1.stuAge = -20; 可以看到这个时候我们给成员变量赋了一个负数,根据实际情况可以我们可以知道年龄是不可能存在负值的,所以这种使用成员变量方式存在缺陷,我们应该在赋值的过程中加入限制,也就是通过一个方法来完成,这也就是前面的set/get方法的作用所在。我们重新对上面的Student类、Teacher类进行改进,并在Test类对其进行使用。代码如下:
/**
这里定义一个Java类用来表示学生

*/
public class Student
{
//学号
private String studentId;

//姓名
private String stuName;

//年龄
private int stuAge;

//性别
private String stuSex;

//无参构造方法
public Student()
{
}

//有参构造方法
public Student(String studentId,String stuName,int stuAge,String stuSex)
{
	this.studentId = studentId;
	this.stuName = stuName;
	this.stuAge = stuAge;
	this.stuSex = stuSex;
}

//set/get系列方法,用来对成员变量的操作

public String getStudentId() {
	return studentId;
}

public void setStudentId(String studentId) {
	this.studentId = studentId;
}

public String getStuName() {
	return stuName;
}

public void setStuName(String stuName) {
	this.stuName = stuName;
}

public int getStuAge() {
	return stuAge;
}

public void setStuAge(int stuAge) {
	this.stuAge = stuAge;
}

public String getStuSex() {
	return stuSex;
}

public void setStuSex(String stuSex) {
	this.stuSex = stuSex;
}


//toString方法将所有的成员变量组合在一起成为字符串
@Override
public String toString() {
	return "Student [studentId=" + studentId + ", stuName=" + stuName + ", stuAge=" + stuAge + ", stuSex=" + stuSex
			+ "]";
}

//学生具有的学习能力
public void study()
{
	//对于学生的学习来说,需要依赖于老师的讲授,这里可以调用Teacher类的讲课能力
	Teacher teacher3 = new Teacher();
	teacher3.teach();
	
	System.out.println( "学习使人进步" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

}
/**
当前这个类表示的是老师

*/
public class Teacher
{
//教师编号
private String teacherId;

//姓名
private String teacherName;

//年龄
private int teacherAge;

//性别
private String teacherSex;

//无参构造方法
public Teacher()
{
}

//有参构造方法
public Teacher(String teacherId,String teacherName,int teacherAge,String teacherSex)
{
	this.teacherId = teacherId;
	this.teacherName = teacherName;
	this.teacherAge = teacherAge;
	this.teacherSex = teacherSex;
}

//set/get系列方法,用来对成员变量的操作

public String getTeacherId() {
	return teacherId;
}

public void setTeacherId(String teacherId) {
	this.teacherId = teacherId;
}

public String getTeacherName() {
	return teacherName;
}

public void setTeacherName(String teacherName) {
	this.teacherName = teacherName;
}

public int getTeacherAge() {
	return teacherAge;
}

public void setTeacherAge(int teacherAge) {
	this.teacherAge = teacherAge;
}

public String getTeacherSex() {
	return teacherSex;
}

public void setTeacherSex(String teacherSex) {
	this.teacherSex = teacherSex;
}


//toString方法将所有的成员变量组合在一起成为字符串
@Override
public String toString() {
	return "Teacher [teacherId=" + teacherId + ", teacherName=" + teacherName + ", teacherAge=" + teacherAge
			+ ", teacherSex=" + teacherSex + "]";
}

//老师具有的讲课能力
public void teach()
{
	System.out.println( "讲授知识" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

}
/**
当前这个类主要作为程序的入口,在当前类中使用
已经编写的其他Java类创建对象
*/
public class Test
{
//程序的入口
public static void main(String[] args)
{

	//创建Student类的对象
	
	//方式一:通过无参构造创建对象
	Student stu1 = new Student();

	stu1.setStudentId( "s001" );
	stu1.setStuName( "张三" );
	stu1.setStuAge( 20 );
	stu1.setStuSex( "男" );
	
	//输出对象stu1中的成员变量的值
	System.out.println( "第一名学生的学号:" + stu1.getStudentId() );
	System.out.println( "第一名学生的姓名:" + stu1.getStuName() );
	System.out.println( "第一名学生的年龄:" + stu1.getStuAge() );
	System.out.println( "第一名学生的性别:" + stu1.getStuSex() );
	
	//将对象stu1的成员变量一次性输出
	System.out.println( "第一名学生信息:" + stu1.toString() );
	
	//通过对象stu1调用学习的方法
	stu1.study();

	//方式二:通过有参构造创建对象
	Student stu2 = new Student( "s002", "李四", 22, "女" );

	//输出对象stu2中的成员变量的值
	System.out.println( "第二名学生的学号:" + stu2.getStudentId() );
	System.out.println( "第二名学生的姓名:" + stu2.getStuName() );
	System.out.println( "第二名学生的年龄:" + stu2.getStuAge() );
	System.out.println( "第二名学生的性别:" + stu2.getStuSex() );
	
	//将对象stu2的成员变量一次性输出
	System.out.println( "第二名学生信息:" + stu2.toString() );
	
	//通过对象stu2调用学习的方法
	stu2.study();
	
	//================================================

	//创建Teacher类的对象

	//方式一:通过无参构造创建对象
	Teacher teacher1 = new Teacher();

	teacher1.setTeacherId("t001");
	teacher1.setTeacherName("王五");
	teacher1.setTeacherAge(30);
	teacher1.setTeacherSex("男");
	
	//输出对象teacher1中的成员变量的值
	System.out.println( "第一名老师的教师编号:" + teacher1.getTeacherId() );
	System.out.println( "第一名老师的姓名:" + teacher1.getTeacherName() );
	System.out.println( "第一名老师的年龄:" + teacher1.getTeacherAge() );
	System.out.println( "第一名老师的性别:" + teacher1.getTeacherSex() );
	
	//将对象teacehr1的成员变量一次性输出
	System.out.println( "第二名学生信息:" + teacher1.toString() );
	
	//通过teacher1对象调用教学方法
	teacher1.teach();

	//方式二:通过有参构造创建对象
	Teacher teacher2 = new Teacher( "t002", "赵六", 29, "女" );

	//输出对象teacher2中的成员变量的值
	System.out.println( "第二名老师的教师编号:" + teacher2.getTeacherId() );
	System.out.println( "第二名老师的姓名:" + teacher2.getTeacherName() );
	System.out.println( "第二名老师的年龄:" + teacher2.getTeacherAge() );
	System.out.println( "第二名老师的性别:" + teacher2.getTeacherSex() );
	
	//将对象teacehr2的成员变量一次性输出
	System.out.println( "第二名学生信息:" + teacher2.toString() );
	
	//通过teacher1对象调用教学方法
	teacher2.teach();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

}
代码运行结果如图:

在上述代码中我们可以看到,对于Student类以及Teacher分别进行了对象的创建操作,并且通过对象分别使用了对应的成员变量以及成员方法,Student类的study方法作用是完成学习的相关操作,学生的学习是以来老师的讲授的,所以在study方法的方法体中通过创建Teacher类的对象,并调用teach方法完成讲授功能,从而让study方法完成对应的学习功能。
5.1.4 this关键字
在进行Java编程的时候,我们需要编写Java类,在这个过程中当我们书写到构造方法,或者set方法的时候,其作用是可以完成对成员变量的赋值操作,在我们定义这些方法时,在方法的形式参数位置需要定义变量,对于变量名的选择,我们需要遵循见名知意的原则,比如下面的代码:
public class Worker
{
//姓名
public String name;

//年龄
public int age;

//有参构造方法
public Worker(String name, int age)
{
	this.name = name;
	this.age = age;
}

//设置名字
public void setName(String name)
{
	this.name = name;
}

//设置年龄
public void setAge(int age)
{
	this.age = age;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

}
在有参构造方法以及set方法的形式参数中定义两个变量name、age,这两名字是比较合适的,但是这个时候存在一个问题,就是在成员变量的位置同样定义了两个变量name、age,在两个方法中我们需要做的事是将方法形式参数上的局部变量赋值给成员变量,这个时候由于出现同名就会出现这样的情况name = name; 这是就分不清楚二者的区别,在这里可以使用this来指代成员变量的那个name也就有了this.name = name;表示将局部变量name的值赋值给成员变量name,这里this起到表示成员变量的作用。
除了具有表示成员变量的作用之外,this还可以用来表示当前类的构造方法,可以通过this来完成构造方法的调用,我们可以看下面的代码:
public class Driver
{
//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Driver()
{
}

//两个参数的有参构造方法
public Driver(String name , int age)
{
	
	this.name = name;
	this.age = age;

	System.out.println( "两个参数的构造方法执行了" );
}

//三个参数的有参构造方法
public Driver(String name , int age, String sex)
{

	//调用两个参数的构造方法
	this(name,age);
	this.sex = sex;

	System.out.println( "三个参数的构造方法执行了" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//使用Driver类的三个参数的构造方法来完成对象创建

	Driver dr = new Driver( "张三",10,"男" );

	System.out.println( "姓名:" + dr.name );
	System.out.println( "年龄:" + dr.age );
	System.out.println( "性别:" + dr.sex );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

}
代码执行效果如图:

通过代码的执行效果我们可以看到,this可代表构造方法,通过this与相应的参数可以调用对应的构造方法。并且在一个构造方法中如果需要使用this调用另外一个构造方法时,那么this的调用操作的代码必须写在该构造方法方法体的第一行位置。
除了this可以表示构造方法之外,在普通的成员方法内部,也存在this,比如我们的get相关的方法,在其方法体中我们需要返回对应的成员变量的值,这个时候在编写对应的方法时,在成员变量的前面省略了this关键字,这个时候this关键表示的是调用该方法的类的对象,从而获取对应的对象中某个成员变量的值,通过下面的代码我们可以看一下get方法中的this的使用,代码如下:
public class Driver
{
//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Driver()
{
}

//对于成员变量name的赋值操作
public void setName(String name)
{
	this.name = name;
}

//获取成员变量name的值
public String getName()
{
	return this.name;
}

//对于成员变量age的赋值操作
public void setAge(int age)
{
	this.age = age;
}

//获取成员变量age的值
public int getAge()
{
	return this.age;
}

//对于成员变量sex的赋值操作
public void setSex(String sex)
{
	this.sex = sex;
}

//获取成员变量sex的值
public String getSex()
{
	return this.sex;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//创建Driver类的对象
Driver dr1 = new Driver( );

	dr1.setName( "张三" );
	dr1.setAge( 35 );
	dr1.setSex( "男" );

	System.out.println( "姓名:" + dr1.getName() );
	System.out.println( "年龄:" + dr1.getAge() );
	System.out.println("性别:"+dr1.getSex());

	Driver dr2 = new Driver();

	dr2.setName("李四");
	dr2.setAge(32);
	dr2.setSex("女");

	System.out.println( "姓名:" + dr2.getName() );
	System.out.println( "年龄:" + dr2.getAge() );
	System.out.println( "性别:" + dr2.getSex() );

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

}运行结果如图:

从上面的代码中我们可以看到咋set与get相关的方法中都存在this,一方面this作为成员变量的标识,另外一方面在类的对象调用对应的set与get方法时,在方法内部this表示的是调用该方法的那个对象实例。除了在set与get方法中我们自定义的普通成员方法,其方法体内也同样存在this,也表示的是调用方法的类的对象实例。比如有下面的代码:
public class Driver
{
//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Driver()
{
}

//该方法将类的对象的相关成员信息拼接在一起形成字符串
public String printMsg()
{
	String msg = "姓名:" + this.name + " 年龄:" + this.age + " 性别:" + this.age;
	return msg;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//创建Driver类的对象
Driver dr1 = new Driver();

	dr1.name = "张三";
	dr1.age = 34;
	dr1.sex = "男";

	String msg1 = dr1.printMsg();
	System.out.println( "dr1对象的成员变量信息:" + msg1 );

	Driver dr2 = new Driver();

	dr2.name = "李四";
	dr2.age = 33;
	dr2.sex = "女";

	String msg2 = dr2.printMsg();
	System.out.println( "dr2对象的成员变量信息:" + msg2 );

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

}
代码运行结果如图:

在代码中当两个Driver类的对象dr1与dr2调用printMsg方法时,在方法内部的this则表示这两个对象,并且从对应的对象中获取出相应的成员变量的值,并完成信息的拼接操作,作为返回值返回出来。
总的来说,this关键字有以下几个作用:
1、在编写代码的过程中,可以用来区分同一个类中由于局部变量与成员变量重名导致的无法区分的问题,这个过程中this可以用来标识成员变量。
2、在对于构造方法的调用过程中,我们可以使用this代指不同的构造方法,各个构造方法具有方法重载的关系,通过不同的参数列表可以区分。使用this完成构造方法的调用操作。
3、在成员方法中,其方法体内存在this主要用来代指调用该成员方法的同一个Java类的不同对象,从而获取相应对象中的成员变量信息。
5.2 面向对象高级特性
在面向对象的编程中,基于面向对象的编程思想,我们将实际问题中各个处理阶段进行划分,并通过将具有相同属性特征以及相同行为功能的个体抽象成为Java类,然后通过Java类完成整个程序的编写,在面向对象的编程中具有一些特性,主要包括:封装特性、继承特性、多态特性。除此之外再编写Java类时,我们还会使用很多的修饰符,比如访问权限修饰符。以及根据对Java类的不同需求,我们分别具有Java类、Java抽闲类、Java接口、Java内部类等相关知识。
5.2.1 类的继承
在面向对象的编程中,类与类之间的继承关系是一个非常重要的特点。我们在编写Java类时会考虑到具体事物的属性特征以及行为功能,将具有相同属性特征以及行为功能的个体,进行抽象的标识,从而形成Java类,比如在校园管理系统中我们就可以根据当前的场景划分出几个Java类,这里面可以有Student类、Teacher类、Worker类,分别标识学校场景中的不同群体,根据实际的情况我们可以编写他们的Java类,代码如下:
public class Worker {

//职工编号
public String workerId;

//姓名
public String name;
//年龄
public int age;
//性别
public String sex;

//工种
public String typeOfWork;


//无参构造方法
public Worker()
{
}

//有参构造方法
public Worker(String workerId, String name, int age, String sex, String typeOfWork) {
	super();
	this.workerId = workerId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.typeOfWork = typeOfWork;
}


//工人的吃饭功能
public void eat()
{
	System.out.print( "人是铁,饭是钢" );
}


//工人的睡觉功能
public void sleep()
{
	System.out.println( "早睡早起,身体好" );
}

//工人的技术工作能力
public void doJob()
{
	System.out.println( "尽职尽责完成任务" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

}
public class Teacher {

//教师编号
public String teacherId;

//姓名
public String name;
//年龄
public int age;
//性别
public String sex;

//学科领域
public String subjectArea;


//无参构造方法
public Teacher()
{
}

//有参构造方法
public Teacher(String teacherId, String name, int age, String sex, String subjectArea) {
	super();
	this.teacherId = teacherId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.subjectArea = subjectArea;
}


//老师的吃饭功能
public void eat()
{
	System.out.print( "人是铁,饭是钢" );
}

//老师的睡觉功能
public void sleep()
{
	System.out.println( "早睡早起,身体好" );
}

//老师的教学功能
public void teach()
{
	System.out.println( "传道授业解惑!" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

}
public class Student {

//学生编号
public String stuId;

//姓名
public String name;
//年龄
public int age;
//性别
public String sex;

//班级
public String classRoom;
//生源地
public String srcAddress;

//无参构造方法
public Student()
{
}

//有参构造方法
public Student(String stuId, String name, int age, String sex, String classRoom, String srcAddress) {
	this.stuId = stuId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.classRoom = classRoom;
	this.srcAddress = srcAddress;
}



//学生的吃饭功能
public void eat()
{
	System.out.print( "人是铁,饭是钢" );
}

//学生的睡觉功能
public void sleep()
{
	System.out.println( "早睡早起,身体好" );
}

//学生的学习功能
public void study()
{
	System.out.println( "学习使人进步!" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

}
在上面的三个Java类中我们分别针对学生群体、教师群体、工人群体进行了属性特征与行为功能的抽象表示,从中我们可以看到,对于部分的属性特征如:姓名、年龄、性别,以及部分的功能特征比如:吃饭能力、睡觉能力,这些是所有的群体所共有的部分,对于这一部分公共的内容,我们可以将其单独的放入一个Java类中,比如我们定义一个类为Person,将上面的三个Java类中公共部分书写在其中。这时我们将Student、Teacher、Worker类中的相同部分去除,转而从Person类中获取。这一操作可以通过关键字extends来完成,也就是一个类去继承另外一个类,对于上面的代码我们可以进行更改,代码如下:
public class Person {

//姓名
public String name;
//年龄
public int age;
//性别
public String sex;


//吃饭功能
public void eat()
{
	System.out.println( "Person类提供的eat功能" );
	System.out.println( "人是铁,饭是钢" );
}

//睡觉功能
public void sleep()
{
	System.out.println( "Person类提供的sleep功能" );
	System.out.println( "早睡早起,身体好" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

}
public class Student extends Person{

//学生编号
public String stuId;
//班级
public String classRoom;
//生源地
public String srcAddress;

//无参构造方法
public Student()
{
}

//有参构造方法
public Student(String stuId, String name, int age, String sex, String classRoom, String srcAddress) {
	this.stuId = stuId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.classRoom = classRoom;
	this.srcAddress = srcAddress;
}


//学生的学习功能
public void study()
{
	System.out.println( "Student类提供的study功能" );
	System.out.println( "学习使人进步!" );
}

@Override
public String toString() {
	return "Student [stuId=" + stuId + ", classRoom=" + classRoom + ", srcAddress=" + srcAddress + ", name=" + name
			+ ", age=" + age + ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

}
public class Teacher extends Person{

//教师编号
public String teacherId;
//学科领域
public String subjectArea;

//无参构造方法
public Teacher()
{
}

//有参构造方法
public Teacher(String teacherId, String name, int age, String sex, String subjectArea) {
	super();
	this.teacherId = teacherId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.subjectArea = subjectArea;
}

//老师的教学功能
public void teach()
{
	System.out.println( "Teacher类提供的teach功能" );
	System.out.println( "传道授业解惑!" );
}

@Override
public String toString() {
	return "Teacher [teacherId=" + teacherId + ", subjectArea=" + subjectArea + ", name=" + name + ", age=" + age
			+ ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

}
public class Worker extends Person{

//职工编号
public String workerId;
//工种
public String typeOfWork;


//无参构造方法
public Worker()
{
}

//有参构造方法
public Worker(String workerId, String name, int age, String sex, String typeOfWork) {
	super();
	this.workerId = workerId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.typeOfWork = typeOfWork;
}

//工人的技术工作能力
public void doJob()
{
	System.out.println( "Worker类提供的doJob功能" );
	System.out.println( "尽职尽责完成任务" );
}

@Override
public String toString() {
	return "Worker [workerId=" + workerId + ", typeOfWork=" + typeOfWork + ", name=" + name + ", age=" + age
			+ ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

}

上面的代码演示了,我们通过类与类的继承操作,完成的代码编写。继承表示的是类与类之间的关系,被继承的类通常被称为父类、超类,主动去继承的类称为子类,通过继承关系,子类可以获取父类的相关的成员变量成员方法。在继承的特性之前,我们可以简化代码编写,提高代码的复用性,使得编程更为简洁明了,当然也会产生代码的耦合性增强的问题,当父类发生变化的时候,子类由于继承关系,会发生同样的变化。
在发生继承关系之后,在子类中可以直接获取父类的相关成员变量与成员方法,下面我们通过代码来验证一下,子类继承父类获取到的内容。代码如下:
public class Test {

public static void main(String[] args)
{
	//测试Student类继承Person类的内容
	Student stu = new Student();
	
	stu.stuId = "s001";
	stu.name = "张三";
	stu.age = 15;
	stu.sex = "男";
	stu.classRoom = "一班";
	
	//将stu对象的信息输出都屏幕中
	System.out.println( stu );
	
	//通过stu对象调用继承到的方法
	stu.eat();
	stu.sleep();
	
	//通过stu对象调用自身方法
	stu.study();
	
	System.out.println("\n===============\n");
	
	//测试Teacher类继承Person类的内容
	
	Teacher tt = new Teacher( "too1", "李四", 30, "女", "数学" );
	
	//将tt对象的信息输出到屏幕中
	System.out.println( tt );
	
	//通过tt对象调用继承到的方法
	tt.eat();
	tt.sleep();
	
	//通过tt对象调用自身方法
	tt.teach();
	
	System.out.println("\n===============\n");
	
	//测试Worker类继承Person类的内容
	
	Worker wk = new Worker( "w001", "王五", 40, "男", "司机" );
	
	//输出wk对象的信息到屏幕中
	System.out.println(wk);
	
	//通过wk对象调用继承的方法
	wk.eat();
	wk.sleep();
	
	//通过wk对象调用自身的方法
	wk.doJob();
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

}
代码的运行结果如图:

从代码的执行结果中我们可以看到,对于Person类的成员变量以及成员方法在发生继承关系之后,直接被Student类、Teacher类、Worker类继承了下来,并且可以直接使用。

5.2.2 Java包的概念
在Java编程的过程中,我们将具有相同属性特征以及行为功能的个体,抽象你成为了Java类,我们通过编写Java类来完成各项业务操作,当多个类都围绕着相同或者相近的一系列功能时,这个时候我们可以将这些类集合到一起形成Java的包。也就是说在同一个Java包中,我们所具有的都是可以完成某个领域的相关的各个类,这样的操作方便我们去进行更好的代码管理。
在Java官方提供的文档中,我们可以看到将常用的从键盘录入以及根据不同的数据结构原理实现出来的不同的集合API,将他们集中到一个包中,这个包就是java.util,从字面意思可以看出util是工具的意思,除此之外为了改善集合在应对多线程编程的时候可能遇到的线程安全问题,需要对其进行特殊的处理,所以专门加入了线程安全的问题处理,从而应对线程安全问题,将这些类都归纳到了java.util.concurrent包中,concurrent则表示的是并发的意思,也就是支持多线程的高并发编程操作。
同样对于我们经常使用的多线程实现类、字符串类、基本数据类型对应的引用数据类型等这些相关的Java类都被集中在了java.lang包中,作为Java编程的基本包而存在,在编写代码时,java.lang包中的Java类是不需要导包操作的。
对于需要进行网络相关操作的Java类责备集中在了java.net包中,这其中包含了大量的关于网络编程中需要涉及到的Java类,比如基于TCP、UDP协议相关的、基于Http协议相关的、网络通信中的Cookie相关、与IP地址协议操作相关的、网络端口、统一文件标识符、统一文件定位符等各种与网络操作相关的Java类,这些类共同构成了net也就是与网络相关的包。
除此之外进行各种文件的处理过程中,我们需要使用到输入流、输出流的相关操作,这些操作也被封装成不同的Java类,共同的归类到了java.io包中,io的含义就是input、output表示进行文件操作过程中对于数据流的处理,这个包中还根据不同的处理形式提供了字节输入流、字节输出流、字符输入流、字符输出流、高效的包装流、文件处理类、序列化处理等相关的各种Java类。这些Java类都是与I/O操作相关的,从而被归类到了java.io这个包中,利于管理和使用。
上面我们介绍了几个Java官方的开发文档中提供给我们在不同的编程场景中需要经常使用到的Java包,这些包根据不同的领域进行划分,从而利于我们在编程过程中快速查找与使用。Java包也是对Java类进行的一种封装,在这些Java的包中,除了Java类以外还存在许多抽象类以及接口,还有就是异常信息相关的类,在Java编程过程中,不仅仅具有的是普通的Java类,还会根据不同的情况进行划分,存在抽象类、接口等不同的区分,通过不同Java类之间的集成或者实现关系,形成一套Java的API体系层次,便于管理以及操作实现,此外在Java编程中还会存在很多的异常或者错误信息,这些不同的错误类型被定义成为不同的Java类,在不同的编程处理过程中,我们根据编程的不同情况会遇到不同的异常错误,需要我们根据文档的介绍,进行相应的处理。
在我们进行项目的开发过程中,我们对于整个项目来说,需要进行统筹管理,无论是项目的需求分析、架构设计、开发进度、测试、项目实施移交等都要进行细致的规划。同样对于编写工程代码来说,我们也需要进行细致的规划,从而方便与代码的有效管理以及编写,从而符合项目的整体进度。在我们进行项目开发的过程中,根据不同的管理思路我们可以进行一下几种方式的操作,首先我们需要定义项目的顶层包名,这一般选择公司域名倒置过来即可,比如www.seowang.cn这样一个域名,当我们需要定义顶层报名时,一般写为cn.seowang,这样的形式,对于顶层之下的其他包,我们可以选择根据业务来进行不同划分,比如现在需要完成的是一个电商项目,大家比较熟悉的京东、淘宝、天猫这些都是比较成熟的项目,这个时候我们可以根据其所涉及的不同业务进行划分,可以划分为注入登录模块、注册模块、会员模块、购物车模块、商品信息模块等等,这样的划分利于从业务角度进行区分,从而方便进行项目代码的管理操作。除此之外我们也可以按照需要使用的不同技术进行划分比如我们在项目开发过程中针对需要使用到的网络操作相关的技术那么我们可以定义一个包为net,将所有与网络相关的类,都放置在其中,针对开发总需要使用到的标识产品信息、用户信息这样的Java类,我们可以定义一个包称为domain,从而进行管理。此外我们还可以针对不同的项目架构模式进行来进行包的划分,模式有很多,比较常用的有MVC、MVP,M表示对于数据的处理模块,构建的模型等,V表示的是数据的展示处理,C与P分别标识数据的控制以及相关处理操作,我们也可以按照这种思路将代码进行拆分,从而利于项目的管理。
总的来说,Java包是针对某个专项领域的Java类而创建的,在其中其中包含处理该领域相关的不同角度的Java类,对于Java包来说,命名时,从组成上来说其符合标识符的命名规则,可以由英文字母、数字、美元符号、下划线组成,但是所有字母全都要小写,名称需要符合见名知意,数字不能开头。在项目开发过程中合理的使用Java包,可以减少管理成本,提高工作效率。
5.2.3 访问权限修饰符
在Java中,存在很多的保留字,也叫关键字,主要就是一些具有特殊含义的单词,这些单词像int、double、boolean等这些都被Java官方占用,我们在记性标识符的命名中不允许使用他们,根据关键字的不同作用可以划分为很多方面,比如表示数据类型的关键字,表示基础逻辑结构的关键字等等,在这其中有一部分关键字,用来作为不同Java类之间进行访问操作时的控制限制,这一部分被称为访问权限修饰符,这些字符主要包含公共的(public)、受保护的(protected)、默认的(不需要写出)、私有的(private),这四种情况用来限制Java类的访问操作。
在一个Java类中,访问权限修饰符主要可以用来修饰符成员变量、成员方法、构造方法这几个部分,在一个类使用另外一个类时,我们主要使用的就是这些组成部分,使用不同的访问权限修饰符可以限制对这些部分的使用操作。对于公共的(public)修饰符的部分,我们从字面意思上可以看到公共的意思就是开放权限最高,其他类可以直接使用使用公共修饰符的部分。对于受保护的(protected)修饰符这一部分,主要指的是在其子类以及同一个包中的Java类之间可以使用,对于不在同一个包中的其他Java类是不可以通过创建对象来进行使用的,此外protected可以表示父类专门为子类提供的成员信息。默认的(不写修饰符)主要用来限制同一个Java包中的各个类可以彼此使用,对于不同的Java包来说,不能直接使用。私有的(private)表示完全属于某个类所有,其他任何的Java类即便是子类也不能直接使用,是当前类所有独有的私有信息。
5.2.4 方法的覆盖
在进行Java编程的过程中,我们根据需要处理的不同业务场景,对整个过程进行分析,并将具有相同属性特征以及行为功能的个体抽象成为Java类,再将不同的Java类中具有的相同的属性特征以及行为功能抽取成为一个类,这个类作为其他类的父类。对于父类中的成员变量与成员方法子类是直接继承下来的,并且是具有强制性的,但是父类在定义功能时,不可能考虑到子类的每个成员方法的具体需求,各个子类根据各自具有的特点,其功能也是存在差异的,也就是说当父类提供的功能不能满足子类的需要时,子类需要具有自己的功能这就存在了一个矛盾,也就是与父类的功能冲突了,面对于这种情况,Java提供了方法覆盖也叫方法重写,通过子类在自己这里对方法进行覆盖,也就是重写父类的某项功能,从而满足子类对于某项功能的特殊需求。
方法覆盖需要按照一定的步骤和格式来完成,方法覆盖是发生在两个Java类之间的操作,不可能在同一个类中发生,这两个类之间具有继承关系,一个类是另外一个类的子类,当子类对于父类提供的某项功能不满足子类需要时,子类需要对父类对应的方法进行重写,这个时候子类的方法与父类的方法的发声明必须完全一样,方法体不同,在子类的方法声明上面可以添加一行标识“@Override”,从字面意思好看看出Override是覆盖重写的意思。这样就可以在子类中将父类的方法进行覆盖,从而应对子类的不同需求。我们使用代码来看一下方法覆盖的具体形式。代码如下:
public class People
{
//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public People()
{
}

//有参构造方法
public People(String name, int age, String sex)
{
	this.name = name;
	this.age = age;
	this.sex = sex;
}

public void eat()
{
	System.out.println( "People类具有的eat方法" );
}

public void sleep()
{
	System.out.println( "People类具有的sleep方法" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

}
public class Worker extends People
{
//工人编号
public String wkId;

//无参构造方法
public Worker()
{
}

//有参构造方法
public Worker(String wkId, String name, int age, String sex)
{
	this.wkId = wkId;
	this.name = name;
	this.age = age;
	this.sex = sex;
}

//Worker类独有的方法
public void doJob()
{
	System.out.println( "Worker类本身具有的doJob方法" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//当Worker类继承了People类之后,没有对父类的方法进行覆盖
//我们看一下代码执行效果

	Worker wk1 = new Worker();

	wk1.eat();
	wk1.sleep();
	wk1.doJob();

	System.out.println(wk1.toString());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

}
代码的运行结果如图:

对于上面的代码运行出来的结果,是我们没有进行方法覆盖时出现的结果,可以看到Worker类从People类中直接继承了相关的功能方法,当发生方法调用操作时,执行的是父类相关的方法。接下来我们在Worker类中加入方法的重写,对Test类的代码不进行修改,然后再运行程序。加入方法覆盖之后的Worker类代码如下:
public class Worker extends People
{
//工人编号
public String wkId;

//无参构造方法
public Worker()
{
}

//有参构造方法
public Worker(String wkId, String name, int age, String sex)
{
	this.wkId = wkId;
	this.name = name;
	this.age = age;
	this.sex = sex;
}

//Worker类独有的方法
public void doJob()
{
	System.out.println( "Worker类本身具有的doJob方法" );
}

@Override
public void eat()
{
	System.out.println( "Worker类对People类eat方法的重写操作" );
}

@Override
public void sleep()
{
	System.out.println( "Worker类对People类sleep方法的重写操作" );
}

@Override
public String toString() {
	return "Worker [wkId=" + wkId + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

}
代码运行的结果如图:

在进行方法重写之后,我们可以看到对于功能在调用的时候直接执行的是子类中的方法,由于覆盖作用,父类的方法不再被执行。同时我们知道所有的Java类直接或者间接的继承了Object类,toString方法时Object类提供的一个将类的对象转变成为字符串的一个方法,在没有覆盖该方法时,该方法输出的是对象的内存地址值,通过覆盖之后,我们将其方法体的内容变为了将所有的成员变量按照字符串的形式拼接在一起,由于我们使用的是无参构造方法创建的实例,所有各个成员变量被赋值成为了初始值。在Object类的源码中我们可以看到toString方法的源码,如下所示:
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
从该方法的方法体可以看到,其输出的是对象的内存地址值。
在Java类中我们最常见到的一种方法就是构造方法,那么构造方法是否也可以进行方法覆盖呢,答案是否定的。对于构造方法来说根据其基本的要求,该方法的名称必须与当前类的名称相同,如果发生方法覆盖操作的话,必须与父类的某个构造方法的方法声明完全相同,这个时候就会出现构造方法名称的冲突,这肯定是不能被允许的,所以对于构造方法来说是不存在方法的覆盖操作的。方法覆盖主要描述的是子类与父类之间针对普通成员方法之间的关系,子类对于某项行为功能对应的成员方法存在特殊的要求,父类不能满足该方法的功能,子类通过方法覆盖来达到目的。
此外方法覆盖不仅仅发生在两个Java类之间,同样也会发生在普通Java类与抽象类和接口之间。抽象类与接口中具有不存在方法体的抽象方法,抽象方法没有方法体,自然也就不能完成对应的具体功能操作,当Java类继承抽象类或者实现接口的时候,这时候同样构成子父类关系,但是充当父类的抽象类与接口由于方法都是抽象的,不具有功能的具体实现,这个时候对于子类来说,必须通过方法覆盖来完善具体的功能,从而达到具体的编程目的,对于这种形式在实际编程中经常使用,同样也可通过这种方式,在抽闲类或者接口中定义一系列的抽闲方法,作为完成一系列具体操作的一个功能规划,对于子类来说,必须按照父类的规划来完成具体的功能实体,从而规范整个开发过程,使得整个项目更加清晰合理。对于Java的API来说,大量使用了这种形式来规范API的不同层次,以方便于在开发过程中的快速记忆以及应用。
5.2.5 引用数据类型的转换
在进行Java编程的过程中,对于基本数据类型来说,需要在进行数学运算时,先统一各个参与运算的数据的类型,运算必须在相同类型的数据之间进行,当我们需要计算一个整数与一个小数的和时,这个时候整数会自动转换为小数,再进行运算,从而得到一个小数的结果。这样的过程中发生了数据类型的自动转换,由小范围的数据类型转换为大范围的数据类型。当我们想要将一个小数赋值给一个整数类型的变量时,这个时候一般是不允许的,但是我们可以通过强制转换的方式来完成,这个时候就会将小数的部分省略掉,只保留整数部分,这个过程中会损失相应的精度。对于基本数据类型来说存在数据类型的转换操作,根据不同的情况来使用。
与基本数据类型相比,在引用数据类型中,同样发生着类似的转换操作,当定义了多个Java类,每个类表示一个数据类型,如果多个Java类之间存在着继承关系,那么这个时候就存在了引用数据类型转换的条件。当我们定义了一个Java类Student时,同时又定义了一个Java类Person,并且Student继承自Person,在这一前提下,我们可以将Person看做一个大范围的数据类型,将Student看做一个小范围的数据类型,二者之间是可以进行转换操作的。比如想要定义一个方法用来输出Student类的对象的成员变量信息,这个时候方法声明的形式参数可以选择Student,这种情况时完全可以理解的,在方法体中输出对应的成员信息,但除此之外,方法的形式参数还可以是Person,这个时候仍然可以完成对于成员变量信息的输出,着主要是因为Person类是Student类的父类,可以用来接收其子类的对象,这个过程中发生了,数据类型的自动提升。我们可以通过代码来看一下上面的这种情况,代码如下:

public class Person {

//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Person() {
	super();
}

//有参构造方法
public Person(String name, int age, String sex) {
	super();
	this.name = name;
	this.age = age;
	this.sex = sex;
}

//set\get系列方法

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

public int getAge() {
	return age;
}

public void setAge(int age) {
	this.age = age;
}

public String getSex() {
	return sex;
}

public void setSex(String sex) {
	this.sex = sex;
}

//该方法用于将对应的类的实例的成员信息输出
@Override
public String toString() {
	return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

}
public class Student extends Person{

//学生编号
public String stuId;

//班级名称
public String classRoom;

//无参构造方法
public Student() {
	super();
}

//有参构造方法
public Student(String stuId, String name, int age, String sex, String classRoom) {
	super();
	this.stuId = stuId;
	this.name = name;
	this.age = age;
	this.sex = sex;
	this.classRoom = classRoom;
}

//set\get系列方法
public String getStuId() {
	return stuId;
}

public void setStuId(String stuId) {
	this.stuId = stuId;
}

public String getClassRoom() {
	return classRoom;
}

public void setClassRoom(String classRoom) {
	this.classRoom = classRoom;
}

//将所有的成员变量信息汇总输出
@Override
public String toString() {
	return "Student [stuId=" + stuId + ", classRoom=" + classRoom + ", name=" + name + ", age=" + age + ", sex="
			+ sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

}
public class Test {

//程序入口
public static void main(String[] args)
{
	
	Student stu = new Student("s001","张三",10,"男","一班");
	printMsg(stu);
	
	System.out.println("=================");
	
	Person p = new Person("王五",20,"女");
	printMsg(p);
	
}

//自定义方法,用来完成对象的成员信息的输出操作
public static void printMsg(Student stu)
{
	System.out.println( "输出Student对象信息" );
	String msg = stu.toString();
	System.out.println(msg);
	
}

//自定义方法,用来完成对象的 成员信息的输出操作
public static void printMsg(Person p)
{
	System.out.println( "输出Person对象信息" );
	String msg = p.toString();
	System.out.println(msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

}
代码的运行效果如图:

对于上面的代码我们可以看到,两个printMsg方法构造了方法重载的关系,所以定义的Student类以及Person类的对象,分别按照不同的方法完成了信息的输出操作。现在我们把代码更改一下,将参数是Student类的那个方法注释掉,再运行代码看一下效果,代码如下:
public class Test {

//程序入口
public static void main(String[] args)
{
	
	Student stu = new Student("s001","张三",10,"男","一班");
	printMsg(stu);
	
	System.out.println("=================");
	
	Person p = new Person("王五",20,"女");
	printMsg(p);
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

// //自定义方法,用来完成对象的成员信息的输出操作
// public static void printMsg(Student stu)
// {
// System.out.println( “输出Student对象信息” );
// String msg = stu.toString();
// System.out.println(msg);
//
// }

//自定义方法,用来完成对象的 成员信息的输出操作
public static void printMsg(Person p)
{
	System.out.println( "输出Person对象信息" );
	String msg = p.toString();
	System.out.println(msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

}
代码的运行结果如图所示:

从代码的执行效果我们可以看到,两个对象的信息同样完成了输出,这是因为Student继承了Person类,当进行代码执行的过程中,自动转换成了父类的类型,在执行过程中由于Student类对toStirng方法进行了重写,所以执行的是子类自己的方法。这个过程都是Java内部自动完成的。
在编程的过程中,除了可以将子类的对象传递给父类的对象之外,我们同样可以完成父类对象向子类对象的转换,这个转换操作不会自动完成,需要使用特定的格式来操作,也就是发生父类对象到子类对象的强转操作,在代码中我们可以通过创建类的实例的方式来看一下类的对象强转操作。现在定义一个方法用来完成实例的创建操作,对于自定义的方法的返回值可以选择为Student类表示方法内部要完成Student类的对象的创建,同样也可选择Person类表示内部要完成的是Person类对象的创建操作,那么是否可以只使用Person类做返回值的方法既可以创建Student类对象也可以创建Person类对象呢?我们可以通过代码来验证一下想法。
public class Demo {

//程序入口
public static void main(String[] args)
{
	//调用方法完成Student类的实例创建操作
	Student stu = createStu();
	System.out.println(stu);
	
	//调用方法完成Person类的实例创建
	Person p = createPer();
	System.out.println(p);
}

//自定义方法用来完成Student类的对象的创建并将对象返回
public static Student createStu()
{
	//创建Student类的实例
	Student stu = new Student("s001","张三",10,"男","一班");
	return stu;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

//自定义方法用来完成Person类的对象的创建并将对象返回
public static Person createPer()
{
//创建Person类的实例
Person p = new Person(“王五”,20,“女”);
return p;
}
}

代码运行结果如图:

从上面的代码我们可以看出两个方法分别完成了对应的类的实例创建操作,那么现在我们是否能在创建Person类的实例的方法中创建一个Student的实例并返回呢?代码如下:
public class Demo {

//程序入口
public static void main(String[] args)
{
	//调用方法创建Student的实例
	Student stu = (Student) createPer();
	System.out.println(stu);
}

//自定义方法用来完成Person类的子类Student类的对象的创建并将对象返回
public static Person createPer()
{
	//创建Student类的实例
	Student stu = new Student("s001","张三",10,"男","一班");
	return stu;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

}
代码的运行结果如图:

对于上面的代码之所以可以完成操作的原因在于,Student类时Person类的子类,在createPer方法中创建了Student类的实例,在return操作的过程中发生了自动的类型转换,将Student类的对象转换称为了Person了的对象,当个通过方法调用使用该功能时,将方法的调用结果使用Student类的对象来接收,这个时候发生了强制类型转换,从而得到了最初创建的Student类的对象。引用数据类型强制类型转换的格式就是在需要转换的值前面添加一个小括号,在小括号中是想要转换成的数据类型。
5.2.6 多态
在进行Java编程的过程中,代码的编译时期以及运行时期,会存在一些区别,尤其是在多个类之间存在的继承或实现关系中,我们使用多态的概念来描述这些区别。所谓多态就是指的是多种形态。
多态一般发生在两个类之间,并且两个类存在继承关系,也就是子类与父类,子类需要重写父类的方法,并且在创建对象时,父类的引用必须指向子类对象,这时候如果使用父类的引用调用子类的重写方法时,这个时候就会存在多态。这是的多种形态表现在代码的编译时期与运行时期,这两个不同时期对于发生重写的方法来说,在编译时期是根据父类中该方法的定义声明来进行的,在代码运行时期需要根据子类重写后的方法来进行代码的运行。所以经常被称为编译时期看父类,运行时期看子类。
我们可以使用代码来验证在在子类与父类之间如果不符合上面的条件时可能出现的一些错误情况。
public class Person
{
//自定义方法完成吃饭功能
public void eat()
{
System.out.println( “Person类的eat方法” );
}

//自定义方法完成睡觉功能
public void sleep()
{
	System.out.println( "Person类的sleep方法" );
}
  • 1
  • 2
  • 3
  • 4
  • 5

}
public class Student extends Person
{
@Override
public void eat()
{
System.out.println( “Student类重写Person类的eat方法” );
}

}
public class Test
{

//程序入口
public static void main(String[] args)
{
	//创建子类的实例对象,并赋值给父类的引用
	Person p = new Student();

	//调用子类已经重写的方法
	p.eat();

	//调用子类为重写的方法
	p.sleep();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

}
代码的运行效果如图:

对Person存在的两个自定义方法,我们在子类中只重写了其中一个,在进行代码运行结果中我们可以看到,两个方法的运行结果是完全不同的,一个是调用的父类相关的功能表示其方法的形态是根据父类来的,另外一个进行了重写的方法,其形态是根据子类来的,这就是发生在子类与父类之间的,对于方法来说,在运行时期与编译时期的形态区别,称为Java的多态。
在子类与父类之间针对方法重写发生的多态是在Java中最常见的多态形式,其需要符合各种情况,就如上面描述的,子类必须继承父类,并在在子类中重写父类的相关方法,还需要父类的引用指向子类的对象,这个时候在通过这个引用调用重写的方法时,就会发生变异时期与运行时期,针对同一个方法存在多种形态,编译时期如果父类中没有定义子类想要重写的方法,那这个时候会编译报错。如果代码我们更改为先这种情况,就会出现问题,代码如下:
public class Person
{
//自定义方法完成吃饭功能
public void eat()
{
System.out.println( “Person类的eat方法” );
}

//自定义方法完成睡觉功能
public void sleep()
{
	System.out.println( "Person类的sleep方法" );
}
  • 1
  • 2
  • 3
  • 4
  • 5

}
public class Student extends Person
{
@Override
public void eat()
{
System.out.println( “Student类重写Person类的eat方法” );
}

@Override
public void study()
{
	System.out.println( "Student类的study方法" );
}
  • 1
  • 2
  • 3
  • 4
  • 5

}
public class Test
{

//程序入口
public static void main(String[] args)
{
	//创建子类的实例对象,并赋值给父类的引用
	Person p = new Student();

	//调用子类已经重写的方法
	p.eat();

	//调用子类为重写的方法
	p.sleep();

	//调用子类的方法
	p.study();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

}
当我们进行编译的时候就会提示错误,如图所示:

之所以出现这个问题的原因是因为,在Student类中,想要重写一个study方法,但是在父类Person中并未定义该方法的存在,在记性编译操作的过程中,就会提示父类中该方法不会被重写,并不会因为在Student类中定义了这个方法,就可以直接编译出来,这个时候需要符合父类的相关规则,也就是在多态中编译时期看父类,子类重写的方法必须是在父类中进行了定义的。
在Java中多态主要指的是针对方法重写来说的,但是除了这种情况之外其实在编程过程中还有其他的地方也是多态的,比如在一个类中,为了完成某项功能而拆分出来的不同情况,各种情况之间使用方法重载的方式来完成,这些重载的方法之间同样也构成了一种多态,根据不同的分支情况去执行对应的代码逻辑,在同一个问题的处理上具有了多种形态,比如下面的代码:
public class Test {

//程序入口
public static void main(String[] args)
{
	//求两个整数的和
	getSum(10,20);
	
	//求三个整数的和
	getSum(10,20,30);
	
	//求四个整数的和
	int result = getSum(10,20,30,40);
	System.out.println( "四个整数的和为:"+result );
}

//自定义方法用来完成两个整数的求和操作
public static void getSum(int a, int b)
{
	int result = a+b;
	
	System.out.println ( "两个整数的和为:"+result );
	
}

//自定义方法用来完成三个整数的求和操作
private static void getSum(int a, int b, int c)
{
	int result = a+b+c;
	
	System.out.println ( "三个个整数的和为:"+result );
}

//自定义方法用来完成四个整数的求和操作,并将结果返回
private static int getSum(int a, int b, int c, int d)
{
	int result = a+b+c+d;
	
	return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

}
这些getSum方法之间,在每种不同需求下的运行中就是多态的。
除此之外,在Java的运算符中有一个比较特殊的存在:“+”,这个运算符我们都知道它可以用来完成数学的加法运算,但是除此之外该元素符还有连接字符串的作用,当我们向屏幕中输出一个信息时,经常在输出信息的前面加上一些文字来标识输出内容,这些标识与数据之间就是通过“+”连接在一起的。对于“+”运算符来说,也是多态的体现。
5.2.7 静态
在Java中有一个关键字static,是静态的意思,称为静态的修饰符,使用静态修饰符可以对Java类中的成员进行修饰。这对一系列的事物进行分析,将具有相同属性特征以及行为功能的个体集中在一起,使用Java类来表示,对于static来说可以用来修饰成员变量与成员方法,那么使用静态修饰具有什么作用呢?对于代码会产生那些影响呢?
对于一个Java类来说,其成员变量时表示一类事物的属性特征,当我们需要创建类的实例时,这个时候会通过构造方法给各个成员变量进行赋值操作,在每个类的对象中都存在对应的成员变量信息,并且这些变量信息在各个对象之间是彼此独立的。因为在创建类的实例的过程中,JVM会在堆内存中开辟一块区域,在这个区域中存在的是类中的各个成员变量并且各个成员变量按照构造方法的指定值进行了初始化,这个操作就发生在new操作之后。但当使用静态修饰符了某个成员变量之后,该成员变量的初始化不是在new操作的过程中完成的,在Java代码的运行过程中首先需要先将Java类的字节码加载到内存中,然后才可以使用该类中代码,比如方法调用、创建对象、变量使用等。当使用static修饰某个成员变量之后,改变了的初始化会提前完成,也就是在类的加载过程中,在JVM的内存中存在一个区域成为方法区,在这个区域中还划分了一个静态变量区,当某个成员变量使用了静态修饰符之后,就会提前被加载到这个区域完成初始化操作,并且即使在创建该类的对象时,也不会再进行加载,被静态修饰的变量会隶属于整个类所有,不是具体的类的某个对象。对于成员变量来说,使用了static修饰,会产生的影响就是,一、该变量加载到内存的时间会提前,一般是跟着类的加载而加载并完成初始化,对于未使用static修饰符的变量是在执行new操作的时候完成初始化的。二、未使用static修饰的变量隶属于创建的类的具体实例,各个实例之间的成员变量信息彼此不交叉,互不影响,使用了static修饰的成员变量会被该类所有的成员变量所共同拥有。
除了static修饰符成员变量,还经常被用来修饰成员方法,在类的加载过程中,在内存的方法区中存在一个静态方法区的划分,使用了static修饰符的方法,在类的加载过程中,会将方法的相关信息存储区到该静态方法区中,其他非静态的方法会在方法区中另外开辟一个区域来存储,并使用类作为标识。对于使用了静态修饰符的成员方法,同样也会被提前加载到内存中的,其他方法需要在类的实例进行方法调用的时候才会进行加载,所以如果想要使用非静态方法的时候必须先进行类的实例创建,通过实例去调用对应的非静态修饰符方法,对于使用了静态修饰的方法,可以不受限制,直接使用类名称调用即可。
对于使用静态修饰符的Java了的成员来说,其属于类的字节码文件对象所有,也就是在编译时期产生的字节码文件,所有在进行使用的时候,无需创建类的实例,直接使用类名调用即可,其实就是通过类的字节码对象进行的调用。此外在静态修饰的成员方法中直接使用其他的成员方法或者成员变量时,该成员方法或者变量也必须使用静态修饰符,这也就是为什么在一开始编写Java代码时,在Test类中除了程序入口main方法之外,其他方法都是用了static修饰,主要是因为这些方法要在main方法中直接进行调用操作,而且main方法是使用static修饰的。
我们通过代码可以对比一下,使用静态修饰语不使用静态修饰符的成员,在使用过程中的区别。代码如下:
在同一个Java类中的直接方法调用
public class Test {

public static void main(String[] args)
{
	//直接使用本类的方法
	doJob();

	doWork();
	
}

//与main方法并列定义一个方法,在main方法中直接使用
public static void doJob()
{
	System.out.println( "静态方法中直接使用的成员必须是static修饰的!" );
}

//定义一个非静态的方法
public void doWork()
{
	System.out.println( "非静态修饰的方法必须使用类的对象调用,不能在静态方法中直接使用!" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

}
代码进行编译的过程中提示了错误:

从错误的信息中我们可以验证,在静态方法中直接调用的方法,也必须是静态修饰的。
在一个类中使用另外一个类的成员
public class Student {

//班级名称
public static String classRoom = "一班";

//学员编号
public String stuId;

//学员姓名
public String name;

//学员年龄
public int age;

//学员性别
public String sex;

//无参构造方法
public Student() {
	super();
}

//有参构造方法
public Student(String stuId, String name, int age, String sex) {
	super();
	this.stuId = stuId;
	this.name = name;
	this.age = age;
	this.sex = sex;
}

//使用static修饰符的成员方法
public static void getSum(int a, int b)
{
	int result = a+b;
	System.out.println( "两个整数的和为:"+result );
}

//未使用static修饰符的成员方法
public void study()
{
	System.out.println( "学习使人进步" );
}

@Override
public String toString() {
	return "Student [classRoom="+ classRoom +", stuId=" + stuId + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

}
public class Test {

public static void main(String[] args)
{
	//直接使用Student类的静态修饰的成员

	System.out.println( "班级名称:"+Student.classRoom );

	Student.getSum(10,20);

	//使用Student类非静态修饰的成员
	Student stu = new Student();

	stu.name = "张三";
	stu.age = 10;
	stu.sex = "男";

	System.out.println( "学员信息:"+stu );

	stu.study();
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

}
代码运行结果如图:

上面就是在Java类中使用静态修饰符与不使用静态修饰符的区别。

5.2.8 final关键字
在Java编程的过程中,final关键字是最终的意思,经常用来修饰Java类、成员变量、成员方法等,在修饰符不同部分时,其具有的作用各不相同,如果在定义Java类时使用final进行修饰符,表示这个类时最终的一个类,也就是说在该类后面不应该再有子类,所以使用final修饰符的Java类不可以被继承。此外final用来修饰成员变量,这个时候该成员变量称为了一个常量,不可以再进行更改操作。当final用来修饰符成员方法时,表示在发生类的继承关系时,该方法不能被子类重写。
下面我们使用代码验证一下final修饰符Java类时,对于Java类的影响。
public final class Person
{

//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Person() {
	super();
}

//有参构造方法
public Person(String name, int age, String sex) {
	super();
	this.name = name;
	this.age = age;
	this.sex = sex;
}

public void eat()
{
	System.out.println( "Person类中的eat功能" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

}
public class Student extends Person
{
//学生编号
public String stuId;

//班级名称
public String classRoom;
  • 1
  • 2

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//创建Student的实例
Student stu = new Student();
}

}
当我们进行代码编译的时候就会出现编译错误,如图:

由于Person类使用了final修饰,该类不能被Student继承,Person称为了一个最终类。我们将代码进行一些更改,去除Person类的final修饰,在成员方法以及成员变量上添加final修饰,代码如下:
public class Person
{

//姓名
public String name;

//年龄
public int age;

//性别
public String sex;

//无参构造方法
public Person() {
	super();
}

//有参构造方法
public Person(String name, int age, String sex) {
	super();
	this.name = name;
	this.age = age;
	this.sex = sex;
}

public final void eat()
{
	System.out.println( "Person类中的eat功能" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

}
public class Student extends Person
{
//学生编号
public String stuId;

//班级名称
public final String classRoom = "一班";

@Override
public void eat()
{
	System.out.println( "Student类重写Person类中的eat功能" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

}
public class Test
{
//程序入口
public static void main(String[] args)
{
//创建Student的实例
Student stu = new Student();
stu.classRoom = “二班”;

}
  • 1

}
代码进行编译时,同样提示错误,如图:

从上面的信息中我们可以看出,对于Person类中的eat方法使用了final修饰,使得在Student继承Person类之后,不能对其进行重写操作,否则会出现编译错误。在Student类中对于成员变量classRoom使用了final修饰,在创建Student类的实例之后,是不能对常量进行再次修改的,因为常量只能进行一次赋值操作。
5.2.9 抽象类
在前面的自定义方法中我们讲解了自定义方法的基本格式,其格式为:
修饰符 返回值类型 方法名称(形式参数列表)
{
方法体
}
这是我们进行自定义方法时需要遵循的基本格式,但是当我们对于需要实现的某个具体功能不能明确其功能的具体实现,也就是说方法体中没有具体的代码,这个时候方法体可以被省略,此时只有方法声明的部分是有作用的,那么此时方法被称为抽象方法。这种情况是存在的,我们前面编写的Java类都是成员变量、成员方法都是完整的,可以很好的表述一类事物的属性特征以及行为功能。但是实际开发过程中,处于整体编程的考虑,需要规划进行一定的规划,这个时候对于某些功能的实现是不需要实现的。这个时候我们需要定义抽象的方法来处理这种情况,在Java类中加入抽象方法,这个类也就成为了抽象类。
对于抽象类来说,具有成员变量、无参构造方法、有参构造方法、成员方法、抽象成员方法等部分组成,相对于前面的Java类来说,仅仅多出一类抽象方法。当我们进行项目开发时,比如在一家公司中同时存在多个项目,有的项目针对少儿人群,有的项目针对年轻人员,有的项目主要针对老年人,那么对于这些项目来说都会存在登录、注册这样的环节,为了更好的管理项目,统一开发的标准,经常会定义一些抽象类,作为登录、注册模块的功能实现的顶层类,这个时候就需要考虑,针对不同的人群,其登录与注册具体的实现细节是不同的,需要各个项目的开发人员根据具体的情况进行区别对待,但是毕竟过程是相似的,比如进行用户名、密码的检查,网络发送,异常情况处理等等。这个时候就可以定义一系列的抽象类,用于规范开发。可以定义如下的抽象类,代码如下:
public abstract class Login {

//定义检查用户名的抽象方法
public abstract boolean checkUserName(String userName);

//定义检查用户密码的抽象方法
public abstract boolean checkUserPwd(String pwd);

//定义完成登录操作的抽象方法
public abstract void doLogin(String userName, String pwd);

//定义针对异常处理的抽象方法
public abstract void dealWithException();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

}
public abstract class Regist {

//定义检查用户名的抽象方法
public abstract boolean checkUserName(String userName);

//定义检查用户密码的抽象方法
public abstract boolean checkUserPwd(String pwd);

//定义完成注册操作的抽象方法
public abstract void doRegist(String userName, String pwd);

//定义针对异常处理的抽象方法
public abstract void dealWithException();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

}
其中abstract关键字表示抽象的含义,在class关键字之前则表示该类为抽象类,修饰方法时表示该方法为抽象方法,抽象方法是没有方法体的,需要使用分号结尾。当然在抽象类中除了抽象方法之外,其他的成员变量与成员方法需要根据具体的情况来进行选择使用。
抽象类中时具有构造方法,但是作为抽象类时不能直接进行对象的创建操作的,对于抽象类的使用,需要通过其子类来完成。我们可以定义一个Java类用来继承抽象类,并将其中的抽象方法通过方法重写实现出来,从而实现其中的功能。一个Java类继承抽象类之后可以有两个中处理方案,要么将当前类也更改为抽象修饰,要么需要将抽象类中的方法全部实现出来。就如下面的代码所示:
子类将抽象类中的方法挨个实现出来
public class MyLogin extends Login{

@Override
public boolean checkUserName(String userName) {
	System.out.println( "子类实现抽象类的检查用户名的功能" );
	return false;
}

@Override
public boolean checkUserPwd(String pwd) {
	System.out.println( "子类实现抽象类的检查密码的功能" );
	return false;
}

@Override
public void doLogin(String userName, String pwd) {
	System.out.println( "子类实现抽象类的登录的功能" );
}

@Override
public void dealWithException() {
	System.out.println( "子类针对不同的异常情况进行处理" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

}
子类与父类一样,使用abstract修饰,称为抽象类,并且可以在父类的基础上增加新的功能,比如注册过程中需要发送验证码,可以定义对象的抽象方法。
public abstract class MyRegist extends Regist{

//定义发送验证码的抽象方法
public abstract void sendCode();
  • 1
  • 2

}
总的来说,抽象类可以通过定义抽象方法,在进行项目开发的过程中,使用自定义方法很好的进行代码的规范化处理,使得项目更加规整。减少差错以及管理成本。
5.2.10 接口
相对于抽象类来说,接口没有了构造方法,没有了完整的成员方法,其中所有的方法都是定义为抽象的,可以说是更加彻底的进行功能的规划操作。表示的接口的关键字为interface不再是class,接口同样可以完成功能规划,比如上面的登录注册逻辑中,我们同样可以使用接口进行规划操作。代码如下:
public interface Login {

//定义检查用户名的抽象方法
public abstract boolean checkUserName(String userName);

//定义检查用户密码的抽象方法
public abstract boolean checkUserPwd(String pwd);

//定义完成登录操作的抽象方法
public abstract void doLogin(String userName, String pwd);

//定义针对异常处理的抽象方法
public abstract void dealWithException();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

}
public interface Regist {

//定义检查用户名的抽象方法
public abstract boolean checkUserName(String userName);

//定义检查用户密码的抽象方法
public abstract boolean checkUserPwd(String pwd);

//定义完成注册操作的抽象方法
public abstract void doRegist(String userName, String pwd);

//定义针对异常处理的抽象方法
public abstract void dealWithException();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

}
对于抽象类来说,经常被用在部分功能不确定具体实现逻辑的场景中,对于接口可以说,如果是所有的功能都不进行具体实现,只是规划,那么使用接口更为合适。
接口同样不能创建对象,并且不存在构造方法,对于其中的成员变量来说,都是使用public static final 修饰的,也就是说都是静态常量,不需要通过构造方法进行初始化。对于接口来说,其功能的实现也必须通过子类来完成,接口与Java类之间,需要通过实现关系来表示。一个Java类实现接口,并将其中的抽象方法全部实现出来,抽象类也可以实现接口,对于接口来说可以继承其他接口。
Java类实现接口的代码,关键字为implements,表示实现关系,此时Java类必须将每一个抽象方法都实现出来。
public class MyLogin implements Login{

@Override
public boolean checkUserName(String userName) {
	System.out.println( "子类实现抽象类的检查用户名的功能" );
	return false;
}

@Override
public boolean checkUserPwd(String pwd) {
	System.out.println( "子类实现抽象类的检查密码的功能" );
	return false;
}

@Override
public void doLogin(String userName, String pwd) {
	System.out.println( "子类实现抽象类的登录的功能" );
}

@Override
public void dealWithException() {
	System.out.println( "子类针对不同的异常情况进行处理" );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

}
抽象类实现接口,由于抽象类本身是具有抽象方法,可以通过实现接口,将其中的所有抽象继承下来,称为自己功能规划的一个部分。
public abstract class MyRegist implements Regist{

//定义发送验证码的抽象方法
public abstract void sendCode();
  • 1
  • 2

}
接口继承接口,在进行登录注册操作过程中,我们免不了需要对数据进行存储、获取、检查等的相关操作,我们同样可以使用接口进行规定。
//当前是数据操作的接口类
public interface DataJob {

//定义进行数据存储操作的抽象方法
public abstract void saveData();

//定义进行数据获取操作的抽象方法
public abstract Object getData();
  • 1
  • 2
  • 3
  • 4
  • 5

}
public interface DataWork extends DataJob{

//定义数据检查的相关抽象方法
public abstract boolean checkData();
  • 1
  • 2

}
在子接口继承父接口的过程中,同样可以增加更多的抽象方法,来规划功能实现。
接口除了记性功能的规划之外还具有一些其他功能,比如进行功能扩展,在Java的I/O操作中存在两个接口Flushable、Closeable,这两个接口为I/O体系提供了刷新数据以及关闭流的功能扩展。此外对于I/O操作中的Serializable接口,具有的作用是添加序列化标识,在Java类的对象进行序列化操作时,需要实现该接口,在该接口中没有定义任何抽象方法,其主要起到的作用就是表示,使得Java虚拟机可以识别需要序列化的类。
5.2.11 内部类
Java的内部类指的是在一个Java类A的内部再定义一个Java类B,这个类B相对类A来说,被称为类A的内部类,类A相对于类B来说称为外部类。在编程过程中我们经常需要考虑对于代码的封装性,可以使用内部类,将部分代码的实现细节隐藏起来。对于Java内部类与外部类来说,内部类可以直接使用外部类的成员变量、成员方法等相关组成部分,外部类必须通过创建内部类的实例才能使用内部类的成员变量、成员方法等组成部分。
根据内部类在一个类中定义的位置不同,我们可以分为成员内部类以及局部内部类两种情况,所谓的成员内部类,指的是定义在与成员变量并列的位置,可以使用与成员变量相同的修饰符来修饰成员内部类。我们可以通过代码展示一下成员内部类的定义以及使用,代码如下:
//定义一个外部类
public class Outer {

//外部类的成员变量
public String outerName = "OuterClass";

//外部类的成员方法
public void showOuter()
{
	System.out.println( "这里是外部类的成员方法showOuter" );
}

//外部类的成员方法
public void printMsg()
{
	System.out.println( "这里是外部类的方法printMsg" );
	
	//在外部类的方法中使用内部类的成员
	Inner in = new Inner();
	System.out.println( "内部类的成员变量:innerName == "+in.innerName );
	
	//通过内部类的对象调用内部类的成员方法
	in.showInner();
}

//定义一个成员内部类
public class Inner
{
	//内部类的成员变量
	public String innerName = "InnerClass";
	
	//内部类的成员方法
	public void showInner()
	{
		System.out.println( "这里是内部类的成员方法showInner" );
		
		//在内部类的方法中直接使用外部类的成员变量
		System.out.println( "外部类的成员变量:outerName == "+outerName );
		
		//在内部类直接调用外部类的成员方法
		showOuter();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

}
public class OuterTest {

//程序入口
public static void main(String[] args)
{
	
	//通过创建爱你对象完成对相应成员的使用
	Outer oo = new Outer();
	
	System.out.println( "Outer的成员信息:"+oo.outerName );
	
	System.out.println("=============");
	
	//调用Outer类的成员方法
	oo.showOuter();
	System.out.println("=============");
	oo.printMsg();
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

}

代码的运行结果如图:

从执行结果中我们可以看到,在printMsg方法的调用过程中,创建了Inner类的实例,并使用了其对应的成员信息,并且在调用Inner类的成员方法showInner时,在该方法内部直接使用了Outer类的成员变量以及成员方法。在这个过程中我们可以很好的对外隐藏关于内部类的相关信息,从而对于Java代码来说具有更好的封装性。
除了成员内部类之外,我们还可以在一个方法内部定义一个内部类,称为局部内部类,对于局部内部类来说,可以直接使用其所在位置的局部数据成分,在外部类,中依然需要通过创建类的实例来完成对内部类的使用。
//定义一个外部类
public class Outer {

//外部类的成员变量
public String outerName = "OuterClass";

//外部类的成员方法
public void showOuter()
{
	System.out.println( "这里是外部类的成员方法showOuter" );
	//不可以使用另外一个成员方法定义的局部内部类
	//Inner in = new Inner();
}

//外部类的成员方法
public void printMsg()
{
	System.out.println( "这里是外部类的方法printMsg" );
	
	//定义一个局部内部类
	class Inner
	{
		//内部类的成员变量
		public String innerName = "InnerClass";
		
		//内部类的成员方法
		public void showInner()
		{
			System.out.println( "这里是内部类的成员方法showInner" );
			
			//在内部类的方法中直接使用外部类的成员变量
			System.out.println( "外部类的成员变量:outerName == "+outerName );
			
			//在内部类直接调用外部类的成员方法
			showOuter();
		}
	}
	
	//在外部类的方法中使用内部类的成员
	Inner in = new Inner();
	System.out.println( "内部类的成员变量:innerName == "+in.innerName );
	
	//通过内部类的对象调用内部类的成员方法
	in.showInner();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

}
public class OuterTest {

//程序入口
public static void main(String[] args)
{
	
	//通过创建爱你对象完成对相应成员的使用
	Outer oo = new Outer();
	
	System.out.println( "Outer的成员信息:"+oo.outerName );
	
	System.out.println("=============");
	
	//调用Outer类的成员方法
	oo.showOuter();
	System.out.println("=============");
	oo.printMsg();
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

}
代码的运行结果如图:

对于局部内部类来说是与局部变量处于平行位置的,其作用范围只在局部位置,不能在整个外部类中任意使用,所以在showOuter方法中不能使用printMsg方法中定义的局部内部类。
对于局部内部类我们更加常用的一种方式是匿名内部类,所谓的匿名内部类就是不写出该内部类的名称,主要应用在当一个成员方法的形式参数是一个接口时,按照需要我们需要传递一个该接口的实现类给该方法,这个时候我们可以使用内部类的形式,来简化实现类的定义操作。具体代码演示如下:
//定义接口
public interface MyCallBack {

//定义操作成功时,需要执行的抽象方法
public abstract void success();

//定义操作失败时,需要执行的抽象方法
public abstract void error();
  • 1
  • 2
  • 3
  • 4
  • 5

}
//自定义一个外部类
public class Outer {

//该成员方法的形式参数是一个接口
public void doJob(MyCallBack mcb)
{
	//进行一系列的操作,可能成功或者失败
	if(100 > 20)
	{
		mcb.success();
	}
	else
	{
		mcb.error();
	}
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

}
public class Test {

//程序入口
public static void main(String[] args)
{
	
	//创建外部类的实例
	Outer out = new Outer();
	
	out.doJob(new MyCallBack(){

		@Override
		public void success() {
			System.out.println( "操作成功!" );
		}

		@Override
		public void error() {
			System.out.println( "操作失败!" );
		}
		
	});
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

}
代码的执行结果如图:

在上述的代码中,在Test类的main方法中调用Outer类的成员方法时,该方法的形式参数为接口类型,那么可以不需要写出该内部类,而是直接通过 new 接口名称(){ 重写方法 }的形式构建了一个匿名内部类。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号