好像过了很久,现在才来总结第八次课的知识,真应该早些总结的,因为那时候对所学的知识会印象深刻一下。好了,以下进入正题。这节课主要介绍了以下知识:主要是面向对象三大特性——封装、继承、多态,具体有隐藏与封装、继承(软件复用)、super关键字限定、多态、重写父类方法(override)、引用变量的类型和强制类型转换、反射的概念、初始化块的使用。希望这篇文章对读者有所帮助。若有不对的地方,欢迎读者指出,在此谢过了!
一、隐藏与封装
1、限制对成员变量的不合理的访问
2、可进行数据检查,从而有利于保证对象信息的完整性
3、隐藏类的实现细节
二、继承(软件复用)
1、子类继承父类,使用extends关键字,如public class Apple extends Friut{}
2、若定义一个未显式声明父类的类,那么它的父类是Object类是(隐式声明)
3、重写父类方法(override)(方法注解),下面的内容介绍都围绕着这个例子
package sandatexing;
public class Ostrich extends Bird {
@Override
public String fly() {
// return super.fly();
return " can only run";
}
public static String run() {
// return super.fly();
return "ostrich";
}
public static void main(String[] args) {
Ostrich ostrich = new Ostrich();
System.out.println("Ostrich is "+ostrich.fly());
}
}
package sandatexing;
public class Bird {
public Bird(String name) {
}
public String fly() {
return "flying...";
}
public static String run() {
return "static";
}
}
运行结果
Ostrich is can only run
4、继承带来一个坏处:破坏封装,所以:
(1)尽量隐藏父类的内部数据
(2)不要让子类可以随意访问、修改父类方法
(3)尽量不要在父类构造器中调用被子类重写的方法(详见4.5)
三、super关键字限定
1、super.方法名:用于调用父类被覆盖的实例方法。
public class Ostrich extends Bird {
@Override
public String fly() {
// TODO Auto-generated method stub
return super.fly();
}
public static void main(String[] args) {
Ostrich ostrich = new Ostrich();
System.out.println(ostrich.fly());
}
运行结果:
flying...
2、子类不能重写父类静态方法。但是如果你要在子类中用到这个方法,你可以去掉"@Override"
注意:虽然子类不能重写父类静态方法,但是,如果你想用父类的这个镜头方法,可以通过子类对象调用父类的这个静态方法,代码如下(注意这个代码和原始的代码是不同的)如:
package sandatexing;
public class Ostrich extends Bird {
@Override
public String fly() {
return super.fly();
}
//这里没有run()
public static void main(String[] args) {
Ostrich ostrich = new Ostrich();
System.out.println("now it is "+ostrich.run());
}
}
package sandatexing;
public class Bird {
public Bird(String name) {
}
public String fly() {
return "flying...";
}
public static String run() {
return "static";
}
}
now it is static
3、如果想要调用父类构造器,只能在子类构造器里面调用,如:
package sandatexing;
public class Bird {
public Bird() {
System.out.println("我是父类构造器");
}
public String fly() {
return "flying...";
}
public static String run() {
return "static";
}
}
package sandatexing;
public class Ostrich extends Bird {
public Ostrich() {
super();
}
@Override
public String fly() {
return super.fly();
}
public static void main(String[] args) {
Ostrich ostrich = new Ostrich();
System.out.println("now it is "+ostrich.run());
}
}
运行结果:
我是父类构造器
now it is static
4、若子类中声明了与父类相同的实例变量,那么父类的实例变量将被隐藏,如:
package sandatexing;
public class BaseClass {
public String name = "父类的实例变量-name";
public void accessOwner() {
System.out.println("BaseClass" + name);
}
}
package sandatexing;
public class SubClass extends BaseClass {
public int age = 123;
private String name = "隐藏父类的同名变量-name";
// 优先当前类的成员变量,即子类的实例变量会隐藏父类的实例变量
@Override
public void accessOwner() {
System.out.println("SubClass中,子类的实例变量-name的值:" + name);
}
public void accessBase() {
//访问父类的实例变量
System.out.println("现在在子类中,调用父类的同名变量:" + super.name);
}
public static void main(String[] args) {
SubClass sc = new SubClass();
sc.accessOwner();
sc.accessBase();
}
}
运行结果:
SubClass中,子类的实例变量-name的值:隐藏父类的同名变量-name
现在在子类中,调用父类的同名变量:父类的实例变量-name
所以,针对此种情况,会出现内存开销的问题:2块内存:
即创建SubClass对象时,系统会为SubClass对象分配两块内存,一块用于储存子类中定义的的name实例变量,一块储存从BaseClass类中继承的name实例变量
四、多态
1、java引用变量类型:编译时类型和运行时类型,如
BaseClass pBc = new SubClass();
BaseClass是编译时类型,SubClass是运行时类型
2、编译时类型:由声明该变量时使用的类型决定
3、运行时类型:由实际赋值给该变量的对象决定
4、所以,多态可以理解为:编译时类型和运行时类型不同。
余老师(读者可以访问其博客,请点击链接)说得很好:由多态,即编译时类型和运行时类型不同,我们可以联系这些知识点:运行时类型、编译时类型、强制类型转等,这样就可以形成一张知识网络了。
我们来看下面的例子(与3.4的例子稍微不同):package sandatexing;
public class BaseClass {
public String name = "父类的实例变量-name";
public int age = 89;
static String num = "89";
public void accessOwner() {
System.out.println("BaseClass" + name);
}
public void test() {
System.out.println("将要被子类重写的方法,父类被覆盖的方法");
}
public void base() {
System.out.println("父类的普通方法");
}
public static void staticMethod(){
System.out.println("父类的静态方法");
}
}
<pre name="code" class="java">package sandatexing;
public class SubClass extends BaseClass {
public int age = 123;
private String name = "隐藏父类的同名变量-name";
// 优先当前类的成员变量,即子类的实例变量会隐藏父类的实例变量
@Override
public void accessOwner() {
// TODO Auto-generated method stub
System.out.println("SubClass中,子类的实例变量-name的值:" + name);
// super.accessOwner();
}
public void accessBase() {
//访问父类的实例变量
System.out.println("现在在子类中,调用父类的同名变量:" + super.name);
}
public void base() {
// TODO Auto-generated method stub
// super.base();
System.out.println("子类base方法");
}
@Override
public void test() {
// TODO Auto-generated method stub
// super.test();
System.out.println("子类---name字符串的长度:" + name.length());
}
public static void main(String[] args) {
BaseClass pBc = new SubClass();
System.out.println("子类方法:");
pBc.base();
pBc.test();
System.out.println();
//可以调用父类未被重写的方法
System.out.println("调用父类未被重写的方法:");
pBc.staticMethod();
System.out.println();
//不能直接调用子类的实例方法
//pBc.accessBase();
//需要:
System.out.println("调用子类的实例方法:");
((SubClass)pBc).accessBase();
System.out.println();
//调用父类的实例变量
System.out.println("调用父类的实例变量:"+((SubClass) pBc).age);//123
//调用子类的实例变量
System.out.println("调用子类的实例变量"+pBc.age);//89
//当前运行时类型
System.out.println("当前运行时类型:" + pBc.getClass().getName());
//下面的方法不能得出当前运行时类型
// if (pBc instanceof BaseClass) {
//
// System.out.println("运行时类型是BaseClass" + " " + BaseClass.class);
// }
// if(pBc instanceof SubClass){
// System.out.println("运行时类型是SubClass:");
//
// }
}
}
运行结果:
子类方法:
子类base方法
子类---name字符串的长度:14
调用父类未被重写的方法:
父类的静态方法
调用子类的实例方法:
现在在子类中,调用父类的同名变量:父类的实例变量-name
调用父类的实例变量:123
调用子类的实例变量89
当前运行时类型:sandatexing.SubClass
(1)BaseClass pBc = new SubClass();
pBc.base();
pBc.test();
这里pBc 调用的是子类的方法(实例方法或静态方法),而且子类中的这个方法需与父类的对应方法同名,而且要同样是实例方法或是静态方法,且返回值类型需要都一样。但是,如果形参数量、类型如果不同则涉及到重载的内容了,可以具体见下面的例子:
package sandatexing;
public class BaseClass {
public String base() {
return "父类的普通方法";
}
}
package sandatexing;
public class SubClass extends BaseClass {
<span style="white-space:pre"> </span>public String base(int a) {
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>return "子类base方法";
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public static void main(String[] args) {
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>BaseClass pBc = new SubClass();
<span style="white-space:pre"> </span>System.out.println(pBc.base());
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>}
}
运行结果:
父类的普通方法
( 2)子类可以调用父类未被重写的方法,如
System.out.println("调用父类未被重写的方法:");pBc.staticMethod();
( 3)引用变量的强制类型转换:在这个例子中,不能直接调用子类的实例方法/变量,需要对对象进行相应的强制类型转换才能调用子类的实例方法/变量,如:
((SubClass)pBc).accessBase();((SubClass) pBc).age)
(4)查看当前运行时类型,可以用:对象名.getClass().getName()。如:
System.out.println("当前运行时类型:" + pBc.getClass().getName());
这里涉及到一个反射的概念:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
5、需要注意一个问题,先看下面一个例子:
package sandatexing;
public class BaseClass {
public BaseClass() {
test();
}
public void test() {
System.out.println("将要被子类重写的方法,父类被覆盖的方法");
}
}
package sandatexing;
public class SubClass extends BaseClass {
private String name;
@Override
public void test() {
//Exception in thread "main" java.lang.NullPointerException
//因为name还没初始化
System.out.println("子类---name字符串的长度:" + name.length());
}
public static void main(String[] args) {
SubClass sc = new SubClass();
}
}
运行结果:
Exception in thread "main" java.lang.NullPointerException
at sandatexing.SubClass.test(SubClass.java:18)
at sandatexing.BaseClass.<init>(BaseClass.java:10)
at sandatexing.SubClass.<init>(SubClass.java:4)
at sandatexing.SubClass.main(SubClass.java:23)
这是因为创建sc对象时,会先执行BaseClass类中的构造器(里面的test()实际是子类的test()),而此时对象的name实例变量为null,所以会引发这个异常。即使在子类中给name赋初始值值,运行结果还是一样,这个读者可以试一下。所以,尽量不要在父类构造器中调用被子类重写的方法。
6、引用变量的强制类型转换,看下面的例子:
package sandatexing;
public class Qiangzhileixingzhaunhaun {
public static void main(String[] args) {
//多态
Object object = new Integer(100);
//编译时没错误,但运行时有错误
String str = (String)object;
System.out.println(str);
}
}
运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at sandatexing.Qiangzhileixingzhaunhaun.main(Qiangzhileixingzhaunhaun.java:11)
所以,我们可以改善一下:
package sandatexing;
public class Qiangzhileixingzhaunhaun {
public static void main(String[] args) {
//多态
Object object = new Integer(100);
if (object instanceof String) {
String str = (String)object;
System.out.println(str);
}
else if (object instanceof Integer) {
System.out.println(object);
}
}
}
运行结果:
100
五、初始化块
1、static{}先于构造器执行、且只有一个(最新执行,初始化块修饰符只有static)
2、多个普通初始化块按顺序执行
2、多个普通初始化块按顺序执行
3、创建对象时,系统总是先调用类里的初始化块
4、初始化块不接收任何参数,如:package sandatexing;
public class Qiangzhileixingzhaunhaun {
{
int a=6;
System.out.println("a="+a);
}
{
String name="xiaoming";
System.out.println("name="+name);
}
//静态初始化块
static{
boolean isFinished=true;
System.out.println("Finish the job:"+isFinished);
}
public Qiangzhileixingzhaunhaun() {
System.out.println("对象初始化");
}
public static void main(String[] args) {
Qiangzhileixingzhaunhaun q=new Qiangzhileixingzhaunhaun();
}
}
运行结果:
Finish the job:true
a=6
name=xiaoming
对象初始化
请注意结果中的顺序
六、作业
要求:
定义普通人、老师、班主任、学生、学校这些类,提供适当的成员变量、方法用于描述其内部数据和行为特征,并提供主类运行。
场景1:学生在学校上课
场景2:老师给学生布置作业
场景3:班主任要求学生参加学校的文体比赛
场景1:学生在学校上课
场景2:老师给学生布置作业
场景3:班主任要求学生参加学校的文体比赛
我在下面的代码中每个类的成功变量都有很多,比如学生除了有姓名,还有学号、年龄等,班主任要求学生参加的活动不止一个等等这些场景,读者可以自行设计编写。
package homework;
import java.util.ArrayList;
import java.util.List;
public class Education {
public static void main(String[] args) {
School school = new School();
school.setAddress("光明路666号");
school.setName("前进小学");
Student student = new Student();
student.setId(1234);
student.setAge(11);
student.setName("张三");
student.setLearningPlace(school.getName());
//学生在学校上课
System.out.println(student.learn());
Teacher teacher = new Teacher();
teacher.setId(2124);
teacher.setAge(45);
teacher.setName("王老师");
teacher.setTeachingPlace("前进小学5年级教室");
Student student2 = new Student();
student.setId(1235);
student.setAge(12);
student2.setName("李四");
student.setLearningPlace(school.getName());
List<Student> students= new ArrayList<Student>();
students.add(student);
students.add(student2);
teacher.setStudents(students);
//老师给学生布置作业
System.out.println(teacher.teach());
HeadTeacher headTeacher = new HeadTeacher();
headTeacher.setPosition("班主任");
headTeacher.setId(2125);
headTeacher.setAge(46);
headTeacher.setName("王老师");
headTeacher.setTeachingPlace("前进小学5年级教室");
headTeacher.setStudents(students);
Activity activity = new Activity();
activity.setId(301);
activity.setName("篮球比赛");
activity.setMaxParticipant(40);
activity.setParticipants(students);
activity.setStartDate("20160701");
activity.setEndDate("20160707");
//教师要求学生参加活动(传人活动举行的学校名称、活动名称)
System.out.println(headTeacher.doSomething(school.getName(), activity));
}
}
class People{
private String name;
private int age;
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;
}
}
class Student extends People{
private int id;
private String learningPlace;
@Override
public int getAge() {
return super.getAge();
}
@Override
public void setAge(int age) {
super.setAge(age);
}
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLearningPlace() {
return learningPlace;
}
public void setLearningPlace(String learningPlace) {
this.learningPlace = learningPlace;
}
public String learn(){
return "学号为" + this.getId() + "、年龄为" + this.getAge() + "岁的" + this.getName() +
"同学在" + this.getLearningPlace() + "上课";
}
}
class School{
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
class Teacher extends People{
private int id;
private String teachingPlace;
private List<Student> students;
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
@Override
public int getAge() {
return super.getAge();
}
@Override
public void setAge(int age) {
super.setAge(age);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTeachingPlace() {
return teachingPlace;
}
public void setTeachingPlace(String teachingPlace) {
this.teachingPlace = teachingPlace;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public String teach(){
String stus = "";
for (Student stu : this.getStudents()) {
stus += stu.getName() + " ";
}
stus += "等同学";
return "教师id为" + this.getId() + "、年龄为" + this.getAge() + "岁的" + this.getName() + "在"
+ this.getTeachingPlace() + "给" + stus + "布置作业";
}
}
class HeadTeacher extends Teacher{
private String position;
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
@Override
public String getName() {
return super.getName();
}
@Override
public void setName(String name) {
super.setName(name);
}
@Override
public int getAge() {
return super.getAge();
}
@Override
public void setAge(int age) {
super.setAge(age);
}
@Override
public int getId() {
return super.getId();
}
@Override
public void setId(int id) {
super.setId(id);
}
@Override
public String getTeachingPlace() {
return super.getTeachingPlace();
}
@Override
public void setTeachingPlace(String teachingPlace) {
super.setTeachingPlace(teachingPlace);
}
@Override
public List<Student> getStudents() {
return super.getStudents();
}
@Override
public void setStudents(List<Student> students) {
super.setStudents(students);
}
public String doSomething(String schoolName,Activity activity){
String stus = "";
for (Student stu : this.getStudents()) {
stus += stu.getName() + " ";
}
stus += "等同学";
return "教师id为" + this.getId() + "、年龄为" + this.getAge() + "岁的" +
this.getPosition() + this.getName() + "要求" + stus + "参加" +
schoolName + "的" + activity.getName() +"等文体活动" + "\n\t(文体活动id:" +
activity.getId() + ",最大参加人数:" + activity.getMaxParticipant() +
",参加人有:" + stus + ",活动开始时间为:" + activity.getStartDate() +
",活动结束时间为:" + activity.getEndDate() + ")";
}
}
class Activity{
private String name;
private int id;
private int maxParticipant;
private List<Student> participants;
private String startDate;
private String endDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getMaxParticipant() {
return maxParticipant;
}
public void setMaxParticipant(int maxParticipant) {
this.maxParticipant = maxParticipant;
}
public List<Student> getParticipants() {
return participants;
}
public void setParticipants(List<Student> participants) {
this.participants = participants;
}
public String getStartDate() {
return startDate;
}
public void setStartDate(String startDate) {
this.startDate = startDate;
}
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
}
运行结果:
学号为1234、年龄为11岁的张三同学在前进小学上课
教师id为2124、年龄为45岁的王老师在前进小学5年级教室给张三 李四 等同学布置作业
教师id为2125、年龄为46岁的班主任王老师要求张三 李四 等同学参加前进小学的篮球比赛等文体活动
(文体活动id:301,最大参加人数:40,参加人有:张三 李四 等同学,活动开始时间为:20160701,活动结束时间为:20160707)
最后,多谢读者耐心读完我这篇啰嗦的博文!