实验二 面向对象(上)
实验2-1 定义学生类
一、实验描述
1、 考核知识点
名称:类和对象
2、 练习目标
- 掌握类定义的方式
- 掌握如何在类中定义成员变量和成员方法
3、 需求分析
在面向对象的思想中最核心就是对象,在程序中创建对象的前提是需要定义一个类。本实验设计一个表示学生的类,该类具有表示姓名的属性name和表示年龄的属性age,同时还具有表示说话行为的方法speak(),用于输出学生的姓名和年龄。
4、 设计思路(实现原理)
1)使用class关键字定义一个表示学生类型的类,类名为Student。
2)在Student类中定义两个成员变量name和age,分别用来表示姓名和年龄。其中,name的数据类型为String,变量age的数据类型为int。
3)在Student类中定义一个表示说话行为的speak()方法,用于输出学生的姓名和年龄。
二、实验实现
package cn.imust.Example201;
public class Example201 {
public static class Student{
static int age=20;
static String name="范敏";
static void speak(){
System.out.println("我是"+name+",今年"+age+"岁");
}
public static void main(String[] args)
{
speak();
}
}
}
运行结果为截图2-0:
图2-0
三、实验总结
1、Java语言严格区分大小写,class和Class是不同的,在定义类时只能使用class关键字
2、在Student类中,成员变量name是String类型,String表示一个字符串
实验2-2 同一对象被多个变量引用
一、实验描述
1、 考核知识点
名称:对象创建与使用
2、 练习目标
- 掌握如何创建类的对象
- 掌握如何使用两个或者多个变量引用同一个实例对象。
3、 需求分析
在程序中,一个对象可能在多处使用,这样就可能需要有多个变量来引用这个对象。本实验基于实验2-1,创建三个学生对象,它们的引用变量分别是s1、s2和s3,首先分别使用s1和s2引用,为name和age赋值,然后调用speak()方法,最后将s2变量赋值给s3, s3也调用speak()方法。
4、 设计思路(实现原理)
1)编写Example01类
2)在main()方法中,创建Student类的第一个对象,其引用变量为s1,使用s1调用name和age变量分别为它们赋值为“张三”和“19”,然后调用speak()方法。
3)创建Student类的第二个对象,其引用变量为s2,使用s2分别为name和age赋值为“李四”和“20”,然后调用speak()方法。
4)创建Student类的第三个对象,其引用变量为s3,将s2的值赋给s3,然后使用s3调用speak()方法。
二、实验实现
package cn.imust.Example202;
public class Student {
static int age;
static String name;
static void speak(){
System.out.println(name+"今年"+age+"岁了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
s1.name="张三";s1.age=19;s1.speak();
s2.name="李四";s2.age=20;s2.speak();
s3=s2;s3.speak();
}
}运行结果截图2-1:
图2-1 运行结果
三、实验总结
1、Student s3 = s2这句代码的作用是将s2引用的内存地址赋值给s3,换句话说,就是使变量s3和s2引用了同一个Student对象,因此s3.speak()方法和s2.speak()方法打印的结果相同。为了更加深刻地了解这句代码的含义,下面通过一张内存图来演示,具体如图2-2所示。
图2-2 内存图
2、可以使用两个或者多个变量引用同一个实例对象,只要通过其中一个变量对该对象的属性进行修改,使用其它引用变量访问时,访问的都是修改后的属性。
实验2-3 类的封装
一、实验描述
1、 考核知识点
名称:类的封装
2、 练习目标
- 了解为什么要对类进行封装
- 了解如何实现类的封装
3、 需求分析
在实验2-2中,s1对象的年龄是可以随便赋值的,如果将age的值赋值为-30,显然违背了事实。为了解决这类问题,我们需要对类进行封装,防止外界对类中的成员变量随意访问。本实验使用private关键字对成员变量name和age进行私有化,同时分别提供一个setName(String n)和setAge(int a)方法用于外界的访问,其中setAge(int a)中需要对age进行判断。
4、 设计思路(实现原理)
1) 编写测试类Example02,将属性age的值设为-30,演示不合理现象。
2) 对Student类进行修改,将name和age属性使用private修饰,然后定义getName()、setName(String n)、getAge()和setAge(int a)四个对外访问name和age的方法。
3) 在setAge(int a)方法中对传入的参数进行检查,如果输入值为负数,则打印出“设置的年龄不合法”,如果不为负数,才将其设置为age属性的值。
4)对Example02类进行修改,在main()方法中创建Student类的实例对象,通过调用对象的setName(String n)和setAge(int a)方法来设置的name属性和age属性值,并调用speak()方法。
二、实验实现
package cn.imust.Example203;
public class Student {
private static int age;
private static String name;
String getName(){
return name;
}
static void setName(String n){
name=n;
}
int getAge(){
return age;
}
static void setAge(int a){
/*if(a<0)
System.out.println("年龄不合理 !");
else*/ age=a;
}
static void speak(){
System.out.println("我的名字是"+name+",今年"+age+"岁了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student.setAge(-30);
Student.setName("范敏");
Student.speak();
}
}
运行结果截图为图2-3:
图2-3 运行结果
可以看出,当将age的值设置为-30后,程序不会报错,但却违背了现实。
2、对Student类进行封装:
package cn.imust.Example203;
public class Student {
private static int age;
private static String name;
String getName(){
return name;
}
static void setName(String n){
name=n;
}
int getAge(){
return age;
}
static void setAge(int a){
if(a<0)
System.out.println("年龄不合理 !");
else age=a;
}
static void speak(){
System.out.println("我的名字是"+name+",今年"+age+"岁了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student.setAge(-30);
Student.setName("范敏");
Student.speak();
}
}
运行结果截图为图2-4:
图2-4 运行结果
三、实验总结
1、Student的name和age属性使用private关键字修饰为私有后,在Example02类中不能再使用s1.name和s1.age的方式访问这两个属性,只能通过public类型的setName(String n)和setAge(int a)方法进行访问。在上面的代码中,调用setAge(int a)方法时的传入参数为-30,由于参数小于0,会打印出“设置的年龄不合法”,并不会将负数赋值给age属性。由此可见,只要实现了封装就能对外界的访问进行控制,避免对私有变量随意修改而引发问题。
实验2-4 定义有参的构造方法
一、实验描述
1、 考核知识点
名称:构造方法的定义
2、 练习目标
- 掌握有参构造方法的定义方式
- 理解系统会自动分配无参构造方法的情况
3、 需求分析
如果希望在创建对象的时候直接为其属性赋值,可以定义有参的构造方法。有参构造方法指的是在初始化对象时,接受外部传入的值并赋给对象的属性。本实验使用有参构造方法完成对象属性的初始化。
4、 设计思路(实现原理)
1) 定义一个Student类,该类有一个age属性,在类中定义一个有参数的构造方法,该参数用于为age属性赋值。
2) 编写一个测试类Example04,在main()方法中通过有参构造方法创建一个对象。
3) 打印该对象age属性的值。
二、实验实现
1、对Student类进行修改:
public class Student {
int age;
public Student(int Age){
age=Age;
}
public void speak(){
System.out.println("我今年"+age+"岁了。");
} }
2、定义Example04类:
package cn.imust.Example204;
public class Student {
int age;
public Student(int Age){
age=Age;
}
public void speak(){
System.out.println("我今年"+age+"岁了。");
}
Public class Example204{
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student(20);
stu.speak();
}
}
运行结果截图为图2-5:
图2-5 运行结果
三、实验总结
1、从运行结果可以看出,new Student(20)语句调用了有参的构造方法Student(int mAge),动态地将20传递给了age属性。和普通方法一样,构造方法中同样可以接收多个参数,只要在使用new关键字创建对象时,传入数量相同和类型一致的参数,就可以自动地调用对应的构造方法。
2、思考一下:在Example04的main()方法中是否能够使用new Student()创建对象呢?答案是否定的,因为new Student()会调用无参的构造方法,而本实验的Student类中并没有定义无参的构造方法。
有些同学肯定会问,之前的Student类都没有定义无参的构造方法,却能使用new Student()创建对象,本实验为什么不行呢?这是因为一个类中如果没有定义构造方法,系统会默认为其分配一个方法体为空的无参构造方法,而一旦定义了构造方法,系统就不再提供默认的构造方法。本实验中由于我们定义了一个有参的构造方法,所以系统不会默认分配无参的构造方法,此时如果通过new Student()去调用无参的构造方法,程序就会发生错误。
实验2-5 构造方法的重载
一、实验描述
1、 考核知识点
名称:构造方法重载
2、 练习目标
- 掌握如何在类中定义重载的构造方法
3、 需求分析
和普通方法一样,构造方法也可以重载。不同的构造方法,可以为不同的属性进行赋值。本实验通过调用不同构造方法创建对象,并根据构造方法的输出结果对构造方法的重载进行学习。
4、 设计思路(实现原理)
1)对Student类进行修改,在类中定义三个重载的构造方法,包括无参的构造方法,接收一个String类型参数的构造方法,接收String类型和int类型两个参数的构造方法。
2)编写测试类Example05,在main()方法中,分别使用三个重载的构造方法创建三个Student对象。
二、实验实现
1、对Student类进行修改:
public class Student {
public Student(){
System.out.println("无参函数被调用!");
}
public Student(String name){
System.out.println("一个参数函数被调用!");
System.out.println(name);
}
public Student(String name,int age)
{
System.out.println("俩个参数函数被调用!");
System.out.println(name+" "+age);
}
}
2、定义Example05类:
Public class Example205{
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student();
Student stu1=new Student("范敏");
Student stu2=new Student("范敏",20);
}
}
运行结果截图为图2-6:
图2-6 运行结果
三、实验总结
一个类中可以定义多个重载的构造方法,在创建对象时,根据传入参数的不同会调用相应的构造方法。
实验2-6 this关键字访问构造方法
一、实验描述
1、 考核知识点
名称:this关键字的使用
2、 练习目标
- 掌握如何在构造方法中使用this关键字访问重载的构造方法
3、 需求分析
如果一个类中定义了多个重载的构造方法,为了避免在重载的构造方法中重复书写代码,可以在一个构造方法中使用this关键字调用其它的构造方法。本实验使用this关键字调用其他的构造方法。
4、 设计思路(实现原理)
1) 在Student类中创建多个重载的构造方法,包括无参的构造方法和一个参数的构造方法,以及两个参数的构造方法。
2) 在一个参数的构造方法中使用this关键字调用无参构造方法,在两个参数的构造方法中调用一个参数的构造方法。
3) 编写测试类Example06,在main()方法中,调用两个参数的构造方法创建对象,演示构造方法的执行顺序。
二、实验实现
1、对Student类进行修改:
class Student{
public Student(){
System.out.println("无参函数被调用!");
}
public Student(int age){
this();
System.out.println("一个参数函数被调用!");
}
public Student(String name,int age){
this(20);
System.out.println("俩个参数函数被调用!");
}
}2、定义Example06类:
public class Example206 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student("范敏",20);
}
}
运行结果截图为图2-7:
图2-7 运行结果
三、实验总结
1、从运行结果可以看出,三个构造方法都被调用了,为了更加清楚地了解三个构造方法的执行顺序,下面通过一张图例进行说明,如图2-9所示。
图2-8 构造方法的执行顺序
2、在构造方法中,使用 this调用重载构造方法的代码必须放在第一行,否则程序不能通过编译,这就限定了在一个构造方法中只能调用一次重载的构造方法。
3、在构造方法中可以通过this.方法名([参数 …])的方式调用普通的成员方法,但是在普通的成员方法中不能使用this([参数 …])的方式来调用构造方法。
实验2-7 垃圾回收机制
一、实验描述
1、 考核知识点
名称:垃圾回收机制
2、 练习目标
- 掌握垃圾回收机制的特点
- 掌握垃圾回收相关的方法
3、 需求分析
垃圾对象会占用一定的内存空间,当垃圾对象积累到一定程度后,Java虚拟机会自动进行垃圾回收。但是,如果希望程序可以及时通知Java虚拟机回收垃圾对象,可以通过System.gc()方法强制启动垃圾回收器回收垃圾。本实验通过System.gc()方法强制启动垃圾回收器回收垃圾。
4、 设计思路(实现原理)
1) 对Student类进行修改,在类中对finalize()方法进行重写。
2) 编写测试类Example07,创建若干个Student对象,然后调用System.gc()方法通知垃圾回收期回收垃圾,为了确保可以看到垃圾回收的过程,可以在类中编写一个简单的循环语句,延长程序执行时间。
二、实验实现
1、对Student类进行修改:
class Student{
public void finalize(){
System.out.println("对象将作为垃圾回收!");
}
}
2、定义Example07类,代码如下所示:
public class Example207 {
/**
* @param args
*/
public static void main(String[] args) {
Student stu1=new Student();
Student stu2=new Student();
stu1=null;
stu2=null;
System.gc();
for(int i=0;i<1000000;i++){
}
}
}
运行结果截图为图2-9:
图2-9 运行结果
三、实验总结
1、从运行结果可以看到,两个Student对象的finalize()方法都被调用了,这表示两个对象作为垃圾被回收了。如果把System.gc()这行代码注释,会发现命令行窗口不会打印任何内容,这说明对象在变成垃圾后不会被立即回收,同时也验证了System.gc()方法的作用。
2、由于System.gc()方法只是通知Java虚拟机尽快进行垃圾回收,这意味着垃圾回收器也可能不会马上运行,因此,在程序的最后使用了一个for循环来延长程序运行的时间,从而确保能够看到垃圾对象被回收的过程。
3、Student类中定义的 finalize()方法其签名必须是public(protected) void finalize()[throw Throwable]{},这样做的原因会涉及到后面的一些知识,比如类的继承、Object类、方法的重写、异常等等,同学们在学完这些内容后就会明白其中的道理。
实验2-8静态变量
一、实验描述
1、 考核知识点
名称:静态变量
2、 练习目标
- 了解静态变量的作用
- 掌握静态变量的定义和使用方式
3、 需求分析
当多个对象存储的数据相同时,可以使用静态变量的方式存储。例如,有一个Student类具有name、className属性,请根据该类创建出三个Student对象,并将这些对象的className值都设置为“三年级二班”。
4、 设计思路(实现原理)
1) 定义Student类,并在类中定义name和className属性。
2) 编写测试类Example08,在main()方法中创建三个学生对象,并分别为这些对象的name和className属性赋值,然后输出这些对象的name和className值。
3) 对Student类进行修改,将className定义为静态变量。
4) 修改测试类,在main()方法中使用Student.className = “三年级二班”语句为静态变量className进行赋值,然后输出这些对象的name和className值。
为了更好地理解Student类中静态变量className和Student实例对象的关系,下面通过一个图例进行演示,如图2-10所示:
图2-10 静态变量与实例对象的关系
二、实验实现
1、定义Student类:
class Student {
String name;
int className;
void speak(){
System.out.println(name+"是"+className+"班的");
}
}
2、定义Example08类:
public class Example208 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
s1.name="范敏";s1.className=1;s1.speak();
s2.name="袁润";s2.className=6;s2.speak();
s3.name="陈晨";s3.className=1;s3.speak();
}
}
运行结果截图为图2-11:
图2-11 运行结果
3、对Student类进行修改,代码如下所示:
class Student {
String name;
static String className;
void speak(){
System.out.println(name+"是"+className+"班的");
} 4、对Example08类进行修改,代码如下所示:
public class Example208 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
Student.className="三年级二班";
s1.name="范敏";s1.speak();
s2.name="袁润";s2.speak();
s3.name="陈晨";s3.speak();
}
}
运行结果截图为图2-12:
图2-12 运行结果
三、实验总结
1、本实验中,三个Student对象的className属性值均为“三年级二班”,对于这样的相同数据,没有必要在每个对象中都开辟一块空间存储,完全可以在内存中只用一块空间存储,并被一个类的所有实例对象所共享。在Java中提供了一个static关键字,使用static关键字修饰的成员变量称为静态变量,静态变量能被该类所有实例对象共享。
2、静态变量可以使用“类名.静态方法名”的方式访问,也可以通过“对象引用变量.静态方法名”的方式访问,例如本例中的静态变量className,通过Student.className或者s2.className这两种方式访问都是可以的,不过更推荐使用前一种方式。
实验2-9 静态方法中访问类的成员
一、实验描述
1、 考核知识点
名称:静态方法
2、 练习目标
- 了解在静态方法中只能访问类的静态成员,而不能访问非静态成员。
3、 需求分析
在程序中经常会调用方法,但静态方法之间、静态方法和非静态方法之间,它们是否能够互相调用呢?请编写一个测试类,在类中定义若干个静态方法和非静态方法,通过方法之间的相互调用,演示静态方法和非静态方法的调用情况。
4、 设计思路(实现原理)
1)编写Example10类,在类中定义两个静态方法staticMethod1()、staticMethod2(),两个非静态方法nonStaticMethod1()、nonStaticMethod2()
2)在Example10类中,针对定义的四个方法进行互相调用,观察调用情况。
二、实验实现
1、定义Example10类,在类中定义上述的四个方法和一个main()方法:
package cn.imust.Example209;
public class Example209 {
static void Method1(){
}
static void Method2(){
int b=3;
}
void nostaticMethod1(){
int c=4;
}
void nostaticMethod2(){
int d=5;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
2、对静态方法staticMethod1()进行修改,在方法中访问静态方法staticMethod2()。在main()方法中调用静态方法staticMethod1():
package cn.imust.Example209;
public class Example209 {
static void Method1(){
Method2();
}
static void Method2(){
int a=10;
System.out.println(a);
}
void nostaticMethod1(){
int c=4;
System.out.println(c);
}
void nostaticMethod2(){
//static void Method2();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Method1();
}
}
运行结果截图为图2-13:
运行结果
从运行结果可以看到,程序正常执行,这说明在静态方法中可以访问静态方法。
3、对静态方法staticMethod1()进行修改,在方法中访问非静态方法nonStaticMethod1():
package cn.imust.Example209;
public class Example209 {
static void Method1(){
nostaticMethod1();
}
static void Method2(){
int a=10;
System.out.println(a);
}
void nostaticMethod1(){
int c=4;
System.out.println(c);
}
void nostaticMethod2(){
//static void Method2();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Method1();
}
}
运行结果截图为图2-14:
图2-13 运行结果
从错误提示信息可以看到,发生错误的原因是在静态方法中访问了非静态的方法。
4、在staticMethod1()方法中,将代码“nonStaticMethod1()”注释掉,并对非静态方法nonStaticMethod1()进行修改,在方法中分别调用静态方法statiMethod1()和非静态方法nonStaticMethod2()。在main()方法中创建Example10的实例对象,调用nonStaticMethod1()方法:
package cn.imust.Example209;
public class Example209 {
static void Method1(){
System.out.println("静态函数1被调用");
// nostaticMethod1();
}
static void Method2(){
System.out.println("静态函数2被调用");
}
void nostaticMethod1(){
System.out.println("非静态函数1被调用");
Method1();
nostaticMethod2();
}
void nostaticMethod2(){
//static void Method2();
System.out.println("非静态函数2被调用");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example209 e=new Example209();
e.nostaticMethod1();
}
}
运行结果截图为图2-15:
图2-14 运行结果
从运行结果可以看到,程序正常执行,这说明在非静态方法中既可以方法静态方法,也可以访问非静态方法。
三、实验总结
1、在静态方法中只能访问静态方法,在非静态方法中可以访问静态方法和非静态方法。
2、思考一下:在静态方法中是否能够访问静态变量和非静态变量?其实和上面的讲解一样,非静态变量只能通过对象或者对象的引用变量访问,而静态方法在创建对象之前就可以通过类名直接访问,因此在静态方法中不能访问非静态变量,只能访问静态变量。
实验2-10 代码块
一、实验描述
1、 考核知识点
名称:静态代码块
2、 练习目标
- 理解代码块的不同分类
- 掌握不同代码块的作用及其执行时机
3、 需求分析
有时候,需要将某一段关联紧密的或者实现了某一功能的代码封装的一个代码块中。为了让初学者熟悉代码块的应用,本实验将编写一个包含了静态代码块,局部代码块和构造代码块的类,演示不同代码块之间的执行时机。
4、 设计思路(实现原理)
1)编写Example11类,在类中定义一个静态代码块、一个构造代码块、一个无参的构造方法和一个成员方法localBlock(),在localBlock()方法中定义一个局部代码块。
2)创建Example11类的两个实例对象,使用Example11类型的变量e1和e2引用,并通过变量e1和e2调用这两个对象的localBlock()方法。
二、实验实现
定义Example11类,代码如下所示:
package cn.imust.Example10;
public class Example210 {
static{
System.out.println("静态代码块被调用!");
}
public Example210(){
System.out.println("构造方法被调用!");
}
{
System.out.println("构造代码块被调用!");
}
public void localBlock(){
System.out.println("localBlock函数被调用!");
{
System.out.println("局部代码块被调用!");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example210 q1=new Example210();
q1.localBlock();
Example210 q2=new Example210();
q2.localBlock();
}
}
运行结果截图为图2-16:
图2-15 运行结果
三、实验总结
1、静态代码块在加载类的时候执行,由于类只在第一次使用时被加载,且只加载一次,因此静态代码块只执行一次。从运行结果可以看到,虽然创建了两个Example11的实例对象,由于Example11类只会加载一次,所以“静态代码块”只打印一次。
在实际开发中,经常有一些代码需要在类加载时就执行,比如加载数据库驱动,这些代码就应该放在静态代码块中。
2、构造代码块在创建类的实例对象时执行,也就是说每次创建类的实例对象时,都会执行一次构造代码块。从运行结果可以看到,构造代码块优先于构造方法执行,因此在实际开发中,可以把重载构造方法中重复的代码抽取到构造代码块中执行。
3、局部代码块定义在方法中,它在方法被调用的时候执行。使用局部代码块是为了限制变量的生命周期,使变量在使用完毕后被尽快回收,从而节省内存空间。
实验2-11 单例设计模式
一、实验描述
1、 考核知识点
名称:单例设计模式
2、 练习目标
- 了解什么是单例设计模式
- 掌握单例设计模式的特点
3、 需求分析
在程序开发中,经常需要保证类的实例对象只有一个,这时,可以将类设计为单例设计模式。本实验将编写一个实现了单例设计模式的类。
4、 设计思路(实现原理)
1)定义一个类Singleton,为了保证该类只能创建一个实例对象,在类中定义一个私有的构造方法。
2)在类中创建一个该类的实例对象,并且定义一个静态变量INSTANCE(变量名随意)引用此实例对象。
3)为了防止外界使用Singleton.INSTANCE的方式直接访问该实例对象,将INSTANCE变量使用private关键字修饰为私有,同时提供一个用于返回实例对象的静态方法。
二、实验实现
1、定义Singleton类,在类中定义一个私有,无参的构造方法:
2、创建Singleton的一个实例对象,定义一个私有的静态变量INSTANCE引用这个对象:
3、定义一个静态方法getInstance()将实例对象返回:
class Singleton{
private Singleton(){}
private static Singleton INSTANCE=new Singleton();{
}
public static Singleton getInstance(){
return INSTANCE;
}
}
4、定义Example12类,在类的main()方法中调用两次getInstance()方法,获得两个Singleton的实例对象,使用“==”比较这两个对象是否相等,代码如下所示:
public class Example212 {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2);
}
}
运行结果截图为图2-17:
图2-16 运行结果
从运行结果可以看到,两个对象使用“==”比较的结果为 true,说明两次获得的是同一个Singleton的实例对象。
三、实验总结
1、为了更加深刻的理解单例设计模式,下面对单例设计模式的特点进行归纳:
l 类中定义一个无参的构造方法,并且声明为私有
l 在类的内部创建该类的一个实例对象,使用私有的静态变量引用该实例对象
l 定义静态方法返回该实例对象
2、本实验中这种代码格式的单例设计模式被形象的称为饿汉式单例设计模式,这是因为在程序中,无论是否需要该类的实例对象,在类加载时都会创建一个实例对象。
实验2-12 静态内部类成员的定义和访问
一、实验描述
1、 考核知识点
名称:静态内部类
2、 练习目标
- 理解在静态内部类中可以定义静态成员和非静态成员
- 掌握访问静态内部类中静态成员和非静态成员的方式
3、 需求分析
静态内部类中可以定义静态成员和非静态成员,这两者的访问方式有所不同。本实验将设计一个静态内部类,并编写测试类演示如何访问静态内部类中的静态成员和非静态成员。
4、 设计思路(实现原理)
1)定义外部类Outer,在Outer类中定义一个静态内部类Inner。
2)在Inner中定义两个String类型变量,一个静态变量staticField,其值为“静态内部类的静态变量”,一个非静态变量nonStaticField,其值为“静态内部类的非静态变量”。定义两个方法,一个静态方法staticMethod()打印“静态内部类的静态方法”,一个非静态方法nonStaticMethod()打印“静态内部类的非静态方法”。
3)编写Example13类,在类的main()方法中调用Inner类中的变量和方法。
二、实验实现
1、定义外部类Outer和静态内部类Inner,代码如下所示:
class Outer{
static class Inner{
static String staticField="静态内部类的静态变量!";
String nostaticField="静态内部类中的非静态变量";
static void staticMethod(){
System.out.println("静态内部类的静态方法!");
}
void nostaticMethod(){
System.out.println("静态内部类的非静态方法!");
}
}
}
2、定义Example13类,在类的main()方法中,通过“外部类.内部类.静态成员”的方式访问静态变量staticField和静态方法staticMethod(),代码如下所示:
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Outer.Inner.staticField);
Outer.Inner.staticMethod();
}
}
运行结果截图为图2-18:
图2-17 运行结果
3、对类Example13进行修改,在main()方法中创建静态内部类Inner的实例对象,使用Outer.Inner类型的变量inner引用,通过变量inner访问非静态变量nonStaticField和非静态方法nonStaticMethod(),代码如下所示:
public class Example212 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Outer.Inner n=new Outer.Inner();
//System.out.println(Outer.Inner.staticField);
System.out.println(n.nostaticField);
//Outer.Inner.staticMethod();
n.nostaticMethod();
}
}
运行结果截图为图2-19:
图2-18 运行结果
三、实验总结
静态内部类中可以定义静态成员和非静态成员,如果要访问非静态成员,必须通过静态内部类的对象或者对象的引用变量。如果要访问静态成员,则可以直接通过“外部类.静态内部类.静态成员”的方式,不需要创建静态内部类的实例对象。
实验2-13 方法内部类访问局部变量
一、实验描述
1、 考核知识点
名称:方法内部类
2、 练习目标
- 掌握方法内部类中访问的局部变量需要使用final关键字修饰
3、 需求分析
在方法内部类中可以访问外部类的成员变量,同样,在方法内部类中也可以访问其所在方法中定义的局部变量,但是局部变量必须使用final关键字来修饰。本实验将编写一个方法内部类,演示如何在方法内部类中访问局部变量。
4、 设计思路(实现原理)
1)编写Example14类,在类中定义一个test()方法,test()方法中定义一个int类型的局部变量num,其值为5。
2)在test()方法中定义一个方法内部类Inner,在Inner类中定义一个show()方法,方法中访问局部变量num。在test()方法中创建内部类Inner的实例对象,调用其show()方法。
3)在Example14的main()方法中,创建Example14的实例对象,使用变量e引用该对象,通过变量e调用test()方法。
二、实验实现
1、定义Example14类,代码如下所示:
public class Example14{
void test() {
int num = 5;
class Inner {
void show() {
System.out.println("局部变量num的值为" + num);
};
}
Inner inner = new Inner();
inner.show();
}
public static void main(String[] args) {
Example14 e = new Example14();
e.test();
}
}
运行结果如图2-20所示:
图2-19 错误提示信息
从错误提示信息可以看到,发生错误的原因是方法内部类中访问的局部变量num没有被声明为最终类型,即没有使用final关键字修饰。
对程序进行修改,将局部变量num使用 final关键字修饰,再次运行程序,运行结果如图2-23所示。
图2-20 运行结果
三、实验总结
方法内部中访问的局部变量必须使用final关键字修饰,否则程序在编译时会发生错误。