javaSE 基础笔记之高级类特性
第四章 Java高级类特性
学习目标:
² 掌握 Java 中的继承
² 掌握方法的覆盖
² 掌握方法的重载
² 掌握 Java 中的多态
² 掌握 static 和 final 的特性和使用
² 了解内部类的构建和使用
² 理解 Java 的内存分配
一:Java 中的继承
1:extends关键字
前面我们已经学习过了什么是继承,那么在 Java 里面如何来表达继承的关系呢,就是
使用extends关键字,比如:经理这个类继承雇员这个类,示例如下:
1 public class Employee { 2 3 String name; 4 5 Date hireDate; 6 7 Date dateOfBirth; 8 9 String jobTitle; 10 11 int grade; 12 13 ... 14 15 } 16 17 public class Manager extends Employee { 18 19 String department; 20 21 Employee[] subordinates; 22 23 ... 24 25 }
在这样的定义中,Manager 类被定义,具有 Employee 所拥有的所有变量及方法。所有
这些变量和方法都是从父类的定义中继承来的。 所有的程序员需要做的是定义额外特征或规
定将适用的变化。
注意:这种方法是在维护和可靠性方面的一个伟大进步。如果在Employee类中进行修
改,那么,Manager 类就会自动修改,而不需要程序员做任何工作,除了对它进行编译。
2:初始化子类必先初始化父类
在 Java 编程语言中,对象的初始化是非常结构化的,这样做是为了保证安全。在前面
的模块中,看到了当一个特定对象被创建时发生了什么。由于继承性,对象被完成,而且下
述行为按顺序发生:
(1)存储空间被分配并初始化到 0值
(2)进行显式初始化
(3)调用构造方法
(4)层次中的每个类都会发生最后两个步骤,是从最上层开始。Java技术安全模式要
求在子类执行任何东西之前,描述父类的一个对象的各个方面都必须初始化。因此,Java
编程语言总是在执行子构造方法前调用父类构造方法的版本。
有继承的类在运行的时候,一定要记得:初始化子类必先初始化父类,这是 Java 程序
的一个基本运行过程。比如:
1 第1行public class Test extends Parent{ 2 第2行 private String name = "Java私塾"; 3 第3行 private int age = 2; 4 第4行 public Test(){ 5 第5行 age = 1000;//期望能到1000年,呵呵 6 第6行 } 7 第7行 public static void main(String[] args) { 8 第8行 Test t = new Test(); 9 第9行 System.out.println(t.name+"的年龄是"+t.age+"年"); 10 第10行 } 11 第11行} 12 第12行class Parent{ 13 第13行 private int num = 1; 14 第14行 public Parent(){ 15 第15行 System.out.println("现在初始化父类"); 16 第16行 } 17 第17行 public void test(){ 18 第18行 System.out.println("这是父类的test方法"); 19 第19行 } 20 第20行}
上述类的基本运行顺序是:
(1) :先运行到第7行,这是程序的入口
(2) :然后运行到第8行,这里要new一个 Test,就要调用 Test的构造方法
(3) :就运行到第4行,注意:初始化子类必先初始化父类
(4) :要先初始化父类,所以运行到第14 行
(5) :然后是第13行,初始化一个类,必须先初始化它的属性
(6) :然后是第15行
(7) :然后是第16行,表示父类初始化完成
(8) :然后是回到子类,开始初始化属性,因此运行到第 2 行,然后是第3 行
(9) :子类属性初始化完过后,才回到子类的构造方法,执行里面的代码,也就是第 5 行
(10) :然后是第6行,表示new一个 Test实例完成
(11) :然后回到main方法中执行第 9 行
(12) :然后是第10行
运行结果是:
现在初始化父类
Java私塾的年龄是1000年
3:单继承性
单继承性: 当一个类从一个唯一的类继承时, 被称做单继承性。 单继承性使代码更可靠。
接口提供多继承性的好处,而且没有(多继承的)缺点。
Java 编程语言允许一个类仅能继承一个其它类,即一个类只能有一个父类。这个限制
被称做单继承性。单继承性与多继承性的优点是面向对象程序员之间广泛讨论的话题。Java
编程语言加强了单继承性限制而使代码更为可靠,尽管这样有时会增加程序员的工作。后面
会学到一个被叫做接口(interface)的语言特征,它允许多继承性的大部分好处,而不受
其缺点的影响。
使用继承性的子类的一个例子如图所示:

4:构造方法不能被继承
尽管一个子类从父类继承所有的方法和变量,但它不继承构造方法, 掌握这一点很重要。
一个类能得到构造方法,只有两个办法。或者写构造方法,或者根本没有写构造方法,
类有一个默认的构造方法。
5:关键字 super
关键字super 可被用来引用该类的父类,它被用来引用父类的成员变量或方法。父类行
为被调用,就好象该行为是本类的行为一样,而且调用行为不必发生在父类中,它能自动向
上层类追溯。
super关键字的功能:
(1) :点取父类中被子类隐藏了的数据成员
(2) :点取已经覆盖了的方法
(3) :作为方法名表示父类构造方法
例如:
1 public class Employee { 2 3 private String name; 4 5 private int salary; 6 7 public String getDetails() { 8 9 return "Name: " + name + "\nSalary: " + salary; 10 11 } 12 13 } 14 15 public class Manager extends Employee { 16 17 private String department; 18 19 public String getDetails() { 20 21 return super.getDetails() + // 调用父类的方法 22 23 "\nDepartment: " + department; 24 25 } 26 27 }
Employee
attributes
name
address
salary
methods
up_salary
promote
Engineer
Secretary
Manager
attributes
bonus
methods
up_bonus
attributes
car allowance
methods
up_ allowance
promote
Director
请注意,super.method()格式的调用,如果对象已经具有父类类型,那么它的方法的整
个行为都将被调用,也包括其所有负面效果。该方法不必在父类中定义,它也可以从某些祖
先类中继承。也就是说可以从父类的父类去获取,具有追溯性,一直向上去找,直到找到为
止,这是一个很重要的特点。
6:调用父类构造方法
在许多情况下,使用默认构造方法来对父类对象进行初始化。
当然也可以使用super 来显示调用父类的构造方法。
1 public class Employee { 2 3 String name; 4 5 public Employee(String n) { 6 7 name = n; 8 9 } 10 11 } 12 13 public class Manager extends Employee { 14 15 String department; 16 17 public Manager(String s, String d) { 18 19 super(s); 20 21 department = d; 22 23 } 24 25 } 26 27
注意:无论是super还是this,都必须放在构造方法的第一行。
通常要定义一个带参数的构造方法, 并要使用这些参数来控制一个对象的父类部分的构
造。 可能通过从子类构造方法的第一行调用关键字 super的手段调用一个特殊的父类构造方
法作为子类初始化的一部分。要控制具体的构造方法的调用,必须给 super()提供合适的参
数。当不调用带参数的super时,缺省的父类构造方法(即,带 0个参数的构造方法)被隐
含地调用。在这种情况下,如果没有缺省的父类构造方法,将导致编译错误。
1 public class Employee { 2 3 String name; 4 5 public Employee(String n) { 6 7 name = n; 8 9 } 10 11 } 12 13 public class Manager extends Employee { 14 15 String department; 16 17 public Manager(String s, String d) { 18 19 super(s); // 调用父类参数为String类型的构造方法 20 21 department = d; 22 23 } 24 25 }
当被使用时,super 或 this 必须被放在构造方法的第一行。显然,两者不能被放在一
个单独行中,但这种情况事实上不是一个问题。如果写一个构造方法,它既没有调用
super(…)也没有调用this(…),编译器自动插入一个调用到父类构造方法中, 而不带参数。
其它构造方法也能调用super(…)或this(…), 调用一个static方法和构造方法的数据链。
最终发生的是父类构造方法(可能几个)将在链中的任何子类构造方法前执行。
二:方法的覆盖和重载
1:方法的覆盖
1.1:什么是方法的覆盖(Overridden Methods)
在类继承中,子类可以修改从父类继承来的行为,也就是说子类能创建一个与父类方法
有不同功能的方法,但具有相同的:名称、返回类型、参数列表
如果在新类中定义一个方法,其名称、返回类型及参数表正好与父类中方法的名称、返
回类型及参数相匹配,那么,新方法被称做覆盖旧方法。
1.2:示例
如下在 Employee和Manager类中的这些方法:
1 public class Employee { 2 3 String name; 4 5 int salary; 6 7 8 9 public String getDetails() { 10 11 return " Name: " + name + " \n " + "Salary: " + salary; 12 13 } 14 15 } 16 17 18 19 public class Manager extends Employee { 20 21 String department; 22 23 24 25 public String getDetails() { 26 27 return " Name: " + name + " \n " + " Manager of " + department; 28 29 } 30 31 }
Manager类有一个定义的 getDetails()方法,因为它是从 Employee 类中继承的。基本的方
法被子类的版本所代替或覆盖了。
1.3:到底运行哪一个方法?
这里会给我们带来一个麻烦,父子类中有相同的方法,那么在运行时到底调用哪一个方
法呢?假设下述方案:
Employee e = new Employee();
Manager m = new Manager();
如果请求 e.getDetails()和 m.getDetails(),就会调用不同的行为。Employee 对象将
执行与 Employee 有关的 getDetails 版本,Manager 对象将执行与 Manager 有关的
getDetails()版本。
不明显的是如下所示:
Employee e = new Manager();
e.getDetails();
或某些相似效果,比如一个通用方法参数或一个来自异类集合的项。
事实上,你得到与变量的运行时类型(即,变量所引用的对象的类型)相关的行为,而
不是与变量的编译时类型相关的行为。这是面向对象语言的一个重要特征。它也是多态性的
一个特征,并通常被称作虚拟方法调用。
在前例中,被执行的e.getDetails()方法来自对象的真实类型,Manager。
因此规则是:编译时看数据类型,运行时看实际的对象类型(new操作符后跟的构造方
法是哪个类的) 。一句话:new谁就调用谁的方法。
1.4:覆盖方法的规则
记住, 子类的方法的名称以及子类方法参数的顺序必须与父类中的方法的名称以及参数
的顺序相同,以便该方法覆盖父类版本。下述规则适用于覆盖方法:
(1) :覆盖方法的返回类型、方法名称、参数列表必须与它所覆盖的方法的相同。
(2) :覆盖方法不能比它所覆盖的方法访问性差(即访问权限不允许缩小) 。
(3) :覆盖方法不能比它所覆盖的方法抛出更多的异常。
这些规则源自多态性的属性和 Java 编程语言必须保证“类型安全”的需要。考虑一下
这个无效方案:
1 public class Parent { 2 3 public void method() { 4 5 } 6 7 } 8 9 public class Child extends Parent { 10 11 private void method() {//编译就会出错 12 13 } 14 15 } 16 17 public class Test{ 18 19 public void otherMethod() { 20 21 Parent p1 = new Parent(); 22 23 Parent p2 = new Child(); 24 25 p1.method(); 26 27 p2.method(); 28 29 } 30 31 }
Java编程语言语义规定,p2.method()导致方法的 Child版本被执行,但因为方法被声明
为private,p2(声明为Parent)不能访问它。于是,语言语义冲突。
2:方法的重载
假如你必须在不同情况下发送不同的信息给同一个成员方法的话, 该怎么办呢?你可以
通过对此成员方法说明多个版本的方法来实现重载。重载的本质是创建了一个新的成员方
法:你只需给它一个不同的参数列表。
2.1:什么是重载
在同一个Java 类中(包含父类) ,如果出现了方法名称相同,而参数列表不同的情况就
叫做重载。
参数列表不同的情况包括:个数不同、类型不同、顺序不同等等。特别提示,仅仅参数
变量名称不同是不可以的。
2.2:重载示例
如下例所示:
void getArea(int w,int h);
void getArea(float w,float h);
在第二种情况下,成员方法getArea()接受两个浮点变量作为它的参数,编译器根据调
用时的不同参数来决定该调用哪一种成员方法,假如你把两个整数提供给成员方法,就调用
第一个成员方法;假如你把两个浮点数提供给成员方法,第二个成员方法就被调用。
当写代码来调用这些方法中的一个方法时, 便以其会根据提供的参数的类型来选择合适
的方法。
注意:跟成员方法一样,构造方法也可以重载。
2.2:方法的重载的规则
(1) :方法名称必须相同
(2) :参数列表必须不同(个数不同,或类型不同,或参数排列顺序不同) 。
(3) : 方法的返回类型可以相同也可以不相同。 仅仅返回类型不同不足以成为方法的重
载。
注意:调用语句的参数表必须有足够的不同,以至于允许区分出正确的方法被调用。正
常的拓展晋升(如,单精度类型 float 到双精度类型 double)可能被应用,但是这样会导
致在某些条件下的混淆。
2.3:比较方法的覆盖和重载
重载方法:
在一个类(或父子类)中用相同的名字创建多个方法(每个方法的参数表不同)
方法覆盖:
在一个类中创建的方法与父类中方法的名字、返回类型和参数表相同,覆盖是针对两个
类说的,而且必须是子类(或孙类,孙孙类等)覆盖掉父类的方法
三:Java 中的多态
1:多态是什么
多态是同一个行为具有多个不同表现形式或形态的能力。
将经理描述成职员不只是描述这两个类之间的关系的一个简便方法。回想一下,经理类
具有父类职员类的所有属性、成员和方法。这就是说,任何在 Employee 上的合法操作在
Manager 上也合法。如果Employee 有 raiseSalary()和 fire()两个方法,那么 Manager 类
也有。
在这种Manager继承Employee的情况下, 一个Employee既可以是一个普通的Employee
类,也可以是一个 Manager类。也就是说下述表示都是对的:
Employee e = new Employee();
Employee e = new Manager();
从上面可以看到:同一个行为 Employee 具有多个不同的表现形式(既可以是一个普通
的Employee类,也可以是一个 Manager类) ,这就被称为多态。
注意:方法没有多态的说法,严格说多态是类的特性。但是也有对方法说多态的,了解
一下, 比如前面学到的方法覆盖称为动态多态, 是一个运行时问题; 方法重载称为静态多态,
是一个编译时问题。
2:多态与类型
一个对象只有一个格式(是在构造时给它的) 。但是,既然变量能指向不同格式的对象,
那么变量就是多态性的。也就是说一个对象只有一种形式,但一个变量却有多种不同形式。
象大多数面向对象语言一样,Java 实际上允许父类类型的引用变量指向一个子类的对
象。因此,可以说:
Employee e = new Manager()
使用变量 e 是因为,你能访问的对象部分只是 Employee 的一个部分;Manager 的特殊
部分是隐藏的。这是因为编译者应意识到,e 是一个 Employee,而不是一个Manager。因而,
下述情况是不允许的:
e.department = " Finance " ; //非法的,编译时会出错
可能有的人会不理解,为什么明明是new 的一个 Manager,却不能访问 Manager的属性
数据。原因在于编译的时候,变量e是一个 Employee的类型,编译器并不去管运行时 e 指
向的具体对象是一个 Employee 的对象,还是一个 Manager 的对象,所以它只能访问到
Employee 里面定义的属性和方法。所以说编译时看数据类型。
那么要想访问到Manager里面的 department 该怎么办呢?这就需要先对 e 进行强制类
型转换,把它还原成为Manager类型,就可以访问到Manager 里面的属性和方法了,如下:
Employee e = new Manager();
Manager m = (Manager)e;
m.department = “开发部”; //这就是合法的了
3:instanceof运算符
多态性带来了一个问题:如何判断一个变量所实际引用的对象的类型。C++使用
runtime-type information(RTTI),Java使用 instanceof操作符。
instanceof 运算符功能:用来判断某个实例变量是否属于某种类的类型。一旦确定了
变量所引用的对象的类型后,可以将对象恢复给对应的子类变量,以获取对象的完整功能。
示例如下:
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
如果通过Employee类型的引用接受一个对象, 它变不变成 Manager或 Contractor 都可
以。可以象这样用instanceof 来测试:
1 public class Employee extends Object 2 3 public class Manager extends Employee 4 5 public class Contractor extends Employee 6 7 public void method(Employee e) { 8 9 if (e instanceof Manager) { 10 11 //如果雇员是经理,可以做的事情写在这里 12 13 }else if (e instanceof Contractor) { 14 15 //如果雇员是普通的职员,可以做的事情写在这里 16 17 }else { 18 19 //说明是临时雇员,可以做的事情写在这里 20 21 } 22 23 }
4:多态对象的类型转换
在你接收父类的一个引用时,你可以通过使用 instanceof 运算符判定该对象实际上是
你所要的子类,并可以用类型转换该引用的办法来恢复对象的全部功能。
1 public void method(Employee e) { 2 3 if (e instanceof Manager) { 4 5 Manager m = (Manager)e; 6 7 System.out.println( " This is the manager of " + m.department); 8 9 } 10 // rest of operation 11 12 }
如果不用强制类型转换,那么引用 e.department 的尝试就会失败,因为编译器不能将
被称做department的成员定位在Employee类中。
如果不用instanceof 做测试,就会有类型转换失败的危险。通常情况下,类型转换一
个对象引用的尝试是要经过几种检查的:
向上强制类型转换类层次总是允许的,而且事实上不需要强制类型转换运算符。可由简
单的赋值实现。
严格讲不存在向下类型转换,其实就是强制类型转换,编译器必须满足类型转换至少是
可能的这样的条件。比如,任何将 Manager 引用类型转换成 Contractor 引用的尝试是肯定
不允许的,因为 Contractor 不是一个 Manager。类型转换发生的类必须是当前引用类型的
子类。
如果编译器允许类型转换,那么,该引用类型就会在运行时被检查。比如,如果
instanceof 检查从源程序中被省略,而被类型转换的对象实际上不是它应被类型转换进去
的类型,那么,就会发生一个运行时错误(exception)。异常是运行时错误的一种形式,而
且是后面章节的主题。
5:动态绑定
绑定:将一个方法调用同一个方法主体连接到一起就称为“绑定” (Binding) 。
动态绑定:当给对象发送请求时,所引起的具体操作既与请求本身有关又与接受对象有
关。支持相同请求的不同对象可能对请求激发的操作有不同的实现。发送给对象的请求和它
的相应操作在运行时刻的连接就称之为动态绑定(dynamic binding)。
动态绑定是指发送的请求直到运行时刻才受你的具体的实现的约束。因而,在知道任何
有正确接口的对象都将接受此请求时,你可以写一个一般的程序,它期待着那些具有该特定
接口的对象。进一步讲,动态绑定允许你在运行时刻彼此替换有相同接口的对象。这种可替
换性就称为多态( p o l y m o r p h i s m ),它是面向对象系统中的核心概念之一。多
态允许客户对象仅要求其它对象支持特定接口,除此之外对其假设几近于无。多态简化了客
户的定义,使得对象间彼此独立,并可以在运行时刻动态改变它们相互的关系。
若在程序运行以前执行绑定 (由编译器和链接程序,如果有的话) ,就叫作“早期绑定” 。
但是在只有一个Instrument句柄的前提下,编译器不知道具体该调用哪个方法。
解决的方法就是“后期绑定” ,它意味着绑定在运行期间进行,以对象的类型为基础。
后期绑定也叫作“动态绑定”或“运行期绑定” 。若一种语言实现了后期绑定,同时必须提
供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此
时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语
言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插
某些特殊类型的信息。
Java 中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成 final。这意味
着我们通常不必决定是否应进行后期绑定――它是自动发生的。
四:static
1:static修饰符
static修饰符能够与属性、方法和内部类一起使用,表示是“静态”的。
类中的静态变量和静态方法能够与“类名”一起使用,不需要创建一个类的对象来访问
该类的静态成员。所以 static 修饰的变量又称作“类变量” 。这与实例变量不同。实例变量
总是用对象来访问,因为它们的值在对象和对象之间有所不同。
下列示例展示了如何访问一个类的静态变量:
1 class StaticModifier { 2 3 static int i = 10; 4 5 int j; 6 7 8 9 StaticModifier() { 10 11 j = 20; 12 13 } 14 15 } 16 17 public class Test { 18 19 public static void main(String args[]) { 20 21 System.out.println("类变量 i=" + StaticModifier.i); 22 23 StaticModifier s = new StaticModifier(); 24 25 System.out.println("实例变量 j=" + s.j); 26 27 } 28 29 }
上述程序的输出是:
类变量i=10
实例变量j=20
2:static属性的内存分配
在上面的例子中,无需创建类的对象即可访问静态变量 i。之所以会产生这样的结果,
是因为编译器只为整个类创建了一个静态变量的副本,因此它能够用类名进行访问。也就是
说:一个类中,一个 static变量只会有一个内存空间,虽然有多个类实例,但这些类实例中
的这个 static变量会共享同一个内存空间。示例如下:
1 public class Test{ 2 3 static UserModel um = new UserModel(); 4 5 public static void main(String[] args) { 6 7 Test t1 = new Test(); 8 9 t1.um.userName = "张三"; 10 11 Test t2 = new Test(); 12 13 t2.um.userName = "李四"; 14 15 16 17 System.out.println("t1.um.userName=="+t1.um.userName); 18 19 System.out.println("t2.um.userName=="+t2.um.userName); 20 21 } 22 23 } 24 25 class UserModel{ 26 27 public String userName=""; 28 29 }
运行结果:
t1.um.userName==李四
t2.um.userName==李四
为什么会是一样的值呢?就是因为多个实例中的静态变量 um 是共享同一内存空间,
t1.um和 t2.um其实指向的都是同一个内存空间,所以就得到上面的结果了。
要想看看是不是static导致这样的结果, 你可以尝试去掉 UserModel前面的 static,然后
再试一试,看看结果,应该如下:
t1.um.userName==张三
t2.um.userName==李四
还有一点也很重要:static的变量是在类装载的时候就会被初始化。也就是说,只要类
被装载,不管你是否使用了这个 static变量,它都会被初始化。
小结一下:类变量(class variables)用关键字 static修饰,在类加载的时候,分配类变
量的内存,以后在生成类的实例对象时,将共享这块内存(类变量) ,任何一个对象对类变
量的修改,都会影响其它对象。外部有两种访问方式:通过对象来访问或通过类名来访问。
3:static的基本规则
有关静态变量或方法的一些要点如下:
-
-
一个类的静态方法只能访问静态属性
-
一个类的静态方法不能够直接调用非静态方法
-
如访问控制权限允许,static属性和方法可以使用对象名加‘.’方式调用;当然也可以使用实例加‘.’方式调用
-
静态方法中不存在当前对象,因而不能使用“this” ,当然也不能使用”super”;
-
静态方法不能被非静态方法覆盖;
-
构造方法不允许声明为 static的
-
static方法可以用类名而不是引用来访问,如:
1 public class GeneralFunction { 2 3 public static int addUp(int x, int y) { 4 5 return x + y; 6 7 } 8 9 } 10 11 public class UseGeneral { 12 13 public void method() { 14 15 int a = 9; 16 17 int b = 10; 18 19 int c = GeneralFunction.addUp(a, b); 20 21 System.out.println("addUp() gives " + c); 22 23 } 24 25 }
因为 static方法不需它所属的类的任何实例就会被调用, 因此没有 this值。 结果是, static
方法不能访问与它本身的参数以及static变量之外的任何变量,访问非静态变量的尝试会引
起编译错误。
注: 非静态变量只限于实例,并只能通过实例引用被访问。
4:静态初始器——静态块
4.1:什么是静态初始器
静态初始器(Static Initializer)是一个存在与类中方法外面的静态块。静态初始器仅仅
在类装载的时候(第一次使用类的时候)执行一次。
静态初始器的功能是:往往用来初始化静态的类属性。
4.2:示例
1 class Count { 2 3 public static int counter; 4 5 static {//只运行一次 6 7 counter = 123; 8 9 System.out.println("Now in static block."); 10 11 } 12 13 public void test(){ 14 15 System.out.println("test method=="+counter); 16 17 } 18 19 } 20 21 public class Test { 22 23 public static void main(String args[]) { 24 25 System.out.println("counter=" + Count.counter); 26 27 new Count().test(); 28 29 } 30 31 }
运行结果是:
Now in static block.
counter=123
test method==123
5:静态 import
当我们要获取一个随机数时,写法是:
1 public class Test { 2 3 public static void main(String[] args) { 4 5 double randomNum = Math.random(); 6 7 System.out.println("the randomNum=="+randomNum); 8 9 } 10 11 }
从JDK5.0开始可以写为:
1 import static java.lang.Math.random; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 7 double randomNum = random(); 8 9 System.out.println("the randomNum=="+randomNum); 10 11 } 12 13 }
静态引用使我们可以象调用本地方法一样调用一个引入的方法, 当我们需要引入同一个
类的多个方法时,只需写为“import static java.lang.Math.*”即可。这样的引用方式对于枚举
也同样有效。
五:final
1:final修饰符
在Java 中声明类、 属性和方法时, 可使用关键字 final来修饰。 final所标记的成分具有 “终
态”的特征,表示“最终的”意思。
2:final的规则
其具体规定如下:
-
-
final标记的类不能被继承。
-
final标记的方法不能被子类重写。
-
final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次。
-
final 标记的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用。
-
final标记的局部变量可以只声明不赋值,然后再进行一次性的赋值。
-
final 一般用于标记那些通用性的功能、实现方式或取值不能随意被改变的成分,以避免被误用,
-
例如实现数学三角方法、幂运算等功能的方法,以及数学常量π=3.141593、e=2.71828
等。事实上,为确保这终态性,提供了上述方法和常量的 java.lang.Math 类也已被定义为
final的。
需要注意的是,如果将引用类型(即,任何类的类型)的变量标记为 final,那么该变
量不能指向任何其它对象。但可以改变对象的内容,因为只有引用本身是 final的。
如果变量被标记为 final,其结果是使它成为常数。想改变 final 变量的值会导致一个编
译错误。下面是一个正确定义 final变量的例子:
public final int MAX_ARRAY_SIZE = 25;
例final关键字程序:Test.java
1 public final class Test{ 2 3 public static final int TOTAL_NUMBER= 5 ; 4 5 public int id; 6 7 public Test(){ 8 9 id = ++TOTAL_NUMBER;//非法,对final变量TOTAL_NUMBER进行二次赋值了。 10 11 //因为++TOTAL_NUMBER相当于:TOTAL_NUMBER=TOTAL_NUMBER+1 12 13 } 14 15 public static void main(String[] args) { 16 17 final Test t = new Test(); 18 19 final int i= 10; 20 21 final int j; 22 23 j = 20; 24 25 j = 30; //非法,对final变量进行二次赋值 26 27 } 28 29 }
Java 编程语言允许关键字final被应用到类上(放在 class关键字前面) 。如果这样做了,
类便不能被再派生出子类。比如,类 Java.lang.String 就是一个 final 类。这样做是出于安全
原因,因为它保证,如果方法有字符串的引用,它肯定就是类 String的字符串,而不是某个
其它类的字符串,这个类是String的被修改过的子类,因为 String可能被恶意窜改过。
方法也可以被标记为 final。被标记为 final 的方法不能被覆盖。这是由于安全原因。如
果方法具有不能被改变的实现,而且对于对象的一致状态是关键的,那么就要使方法成为
final。被声明为 final 的方法有时被用于优化。编译器能产生直接对方法调用的代码,而不
是通常的涉及运行时查找的虚拟方法调用。
.
六:内部类
1:什么是内部类
内部类(Inner Classes)的概念是在 JDK1.1 版本中开始引入的。在 Java 中,允许在一
个类(或方法、语句块)的内部定义另一个类,后者称为内部类,有时也称为嵌套类(Nested
Classes) 。内部类和外层封装它的类之间存在逻辑上的所属关系,一般只用在定义它的类或
语句块之内,实现一些没有通用意义的功能逻辑,在外部引用它时必须给出完整的名称。
引入内部类的好处在于可使源代码更加清晰并减少类的命名冲突,就好比工厂制定内部
通用的产品或工艺标准,可以取任何名称而不必担心和外界的标准同名,因为其使用范围不
同。内部类是一个有用的特征,因为它们允许将逻辑上同属性的类组合到一起,并在另一个
类中控制一个类的可视性。
下述例子表示使用内部类的共同方法:
1 public class Test { 2 3 public static void main(String[] args) { 4 5 MyFrame mf = new MyFrame("Java私塾"); 6 7 } 8 9 } 10 11 class MyFrame extends Frame { 12 13 Button myButton; 14 15 TextArea myTextArea; 16 17 int count; 18 19 20 21 public MyFrame(String title) { 22 23 super(title); 24 25 myButton = new Button("click me"); 26 27 myTextArea = new TextArea(); 28 29 add(myButton, BorderLayout.CENTER); 30 31 add(myTextArea, BorderLayout.NORTH); 32 33 ButtonListener bList = new ButtonListener(); 34 35 myButton.addActionListener(bList); 36 37 } 38 39 40 41 class ButtonListener implements ActionListener // 这里定义了一个内部类 42 43 { 44 45 public void actionPerformed(ActionEvent e) { 46 47 count++; 48 49 myTextArea.setText("button clicked" + count + "times"); 50 51 } 52 53 } // end of innerclass ButtonListener 54 55 public static void main(String args[]) { 56 57 MyFrame f = new MyFrame("Inner Class Frame"); 58 59 f.setSize(300, 300); 60 61 f.setVisible(true); 62 63 } 64 65 }
前面的例子包含一个类 MyFrame,它包括一个内部类 ButtonListener。编译器生成一个
类文件,MyFrame$ButtonListener.class以及 MyFrame.class。它包含在 MyFrame.class中,是
在类的外部创建的。
2:内部类特点
(1) :嵌套类(内部类)可以体现逻辑上的从属关系。同时对于其他类可以控制内部类对外
不可见等
(2) :外部类的成员变量作用域是整个外部类,包括嵌套类。但外部类不能访问嵌套类的
private成员
(3) :逻辑上相关的类可以在一起,可以有效的实现信息隐藏。
(4) :内部类可以直接访问外部类的成员。可以用此实现多继承!
(5) :编译后,内部类也被编译为单独的类,不过名称为 outclass$inclass的形式。
再来个例子:
1 public class Outer { 2 3 private int size; 4 5 public class Inner{ 6 7 private int counter = 10; 8 9 public void doStuff(){ size++; } 10 11 } 12 13 public static void main(String args[]){ 14 15 Outer outer=new Outer (); 16 17 Inner inner= outer . new Inner(); 18 19 inner.doStuff(); 20 21 System.out.println(outer.size); 22 23 System.out.println(inner.counter); 24 25 // System.out.println(counter); 编译错误,外部类不能访问内部类的 private变量 26 27 } 28 29 } 30 31
3:内部类的分类
内部类按照使用上可以分为四种情形:
(1) :类级:成员式,有static 修饰
(2) :对象级:成员式,普通,无 static 修饰
(3) :本地内部类:局部式
(4) :匿名级:局部式
4:成员式内部类
内部类可以作为外部类的成员,示例如下:
1 public class Outer1{ 2 3 private int size; 4 5 public class Inner{ 6 7 public void dostuff(){ 8 9 size++; 10 11 } 12 13 } 14 15 public void testTheInner(){ 16 17 Inner in=new Inner(); 18 19 in.dostuff(); 20 21 } 22 23 }
成员式内部类如同外部类的一个普通成员。
4.1:成员式内部类的基本规则
(1) :可以有各种修饰符,可以用 4 种权限、static、final、abstract 定义(这点和普通
的类是不同的) ;
(2) :若有static限定,就为类级,否则为对象级。类级可以通过外部类直接访问;对
象级需要先生成外部的对象后才能访问。
(3) :内外部类不能同名
(4) :非静态内部类中不能声明任何 static成员
(5) :内部类可以互相调用,如下:
class A {
class B { } //B、C间可以互相调用
class C { }
}
4.2:成员式内部类的访问
内部类的对象以属性的方式记录其所依赖的外层类对象的引用, 因而可以找到该外层类
对象并访问其成员。该属性是系统自动为非 static 的内部类添加的,名称约定为“外层类
名.this” 。
在其它场合则必须先获得外部类的对象,再由外部类对象加“.new”操作符调用内部
类的构造方法创建内部类的对象,此时依赖关系的双方也可以明确。这样要求是因为:外部
类的static方法中不存在当前对象,或者其它无关类中方法的当前对象类型不符合要求。
(1) :在另一个外部类中使用非静态内部类中定义的方法时,要先创建外部类的对象,
再创建与外部类相关的内部类的对象,再调用内部类的方法,如下所示:
1 class Outer2{ 2 3 private int size; 4 5 class Inner{ 6 7 public void dostuff(){ size++; } 8 9 } 10 11 } 12 13 class TestInner{ 14 15 public static void main(String[] args){ 16 17 Outer2 outer = new Outer2(); 18 19 Outer2.Inner inner=outer.new Inner(); 20 21 inner.dostuff(); 22 23 } 24 25 } 26 27
(2) :static内部类相当于其外部类的 static成分,它的对象与外部类对象间不存在依赖
关系,因此可直接创建。示例如下:
1 class Outer2 { 2 3 private static int size; 4 5 static class Inner { 6 7 public void dostuff() { 8 9 size++; 10 11 System.out.println("size="+size); 12 13 } 14 15 } 16 17 } 18 19 public class Test { 20 21 public static void main(String[] args) { 22 23 Outer2.Inner inner = new Outer2.Inner(); 24 25 inner.dostuff(); 26 27 } 28 29 }
程序运行结果为:
size=1
(3) :由于内部类可以直接访问其外部类的成分,因此当内部类与其外部类中存在同名
属性或方法时,也将导致命名冲突。所以在多层调用时要指明,如下所示:
1 public class Outer3{ 2 3 private int size; 4 5 public class Inner{ 6 7 private int size; 8 9 public void dostuff(int size){ 10 11 size++; //本地的 size; 12 13 this.size; //内部类的 size 14 15 Outer3.this.size++; //外部类的 size 16 17 } 18 19 } 20 21 }
5:本地内部类
本地类(Local class)是定义在代码块中的类.它们只在定义它们的代码块中是可见的.
本地类有几个重要特性:
(1) :仅在定义了它们的代码块中是可见的;
(2) :可以使用定义它们的代码块中的任何本地 final变量;
(3) :本地类不可以是static的,里边也不能定义static成员。
(4) :本地类不可以用public、private、protected修饰,只能使用缺省的。
(5) :本地类可以是abstract的。
示例如下:
1 public final class Outter{ 2 3 public static final int TOTAL_NUMBER= 5 ; 4 5 public int id=123; 6 7 8 9 public void t1(){ 10 11 final int a = 15; 12 13 String s = "t1"; 14 15 16 17 class Inner{ 18 19 public void innerTest(){ 20 21 System.out.println(TOTAL_NUMBER); 22 23 System.out.println(id); 24 25 System.out.println(a); 26 27 //System.out.println(s);不合法,只能访问本地方法的final变量 28 29 } 30 31 } 32 33 new Inner().innerTest(); 34 35 } 36 37 38 39 public static void main(String[] args) { 40 41 Outter t = new Outter (); 42 43 t.t1(); 44 45 } 46 47 }
6:匿名内部类
6.1:匿名内部类是什么
匿名内部类是本地内部类的一种特殊形式,也就是没有类名的内部类,而且具体的类实
现会写在这个内部类里面。
把上面的例子改造一下,如下所示:
1 public final class Test{ 2 3 public static final int TOTAL_NUMBER= 5 ; 4 5 public int id=123; 6 7 8 9 public void t1(){ 10 11 final int a = 15; 12 13 String s = "t1"; 14 15 16 17 new Aclass(){ 18 19 public void testA(){ 20 21 System.out.println(TOTAL_NUMBER); 22 23 System.out.println(id); 24 25 System.out.println(a); 26 27 //System.out.println(s);不合法,只能访问本地方法的final变量 28 29 } 30 31 }.testA(); 32 33 } 34 35 public static void main(String[] args) { 36 37 Test t = new Test(); 38 39 t.t1(); 40 41 } 42 43 } 44 45 class Aclass{ 46 47 public void testA(){} 48 49 }
注意:匿名内部类是在一个语句里面,所以后面需要加“; ” 。
6.2:匿名类的规则
(1) :匿名类没有构造方法;
(2) :匿名类不能定义静态的成员;
(3) :匿名类不能用4种权限、static、final、abstract修饰;
(4) :只可以创建一个匿名类实例
再次示例:
1 public class Outter{ 2 3 public Contents getCont() { 4 5 return new Contents() { 6 7 private int i = 11; 8 9 public int value() { return i; } 10 11 }; 12 13 } 14 15 public static void main(String[] args) { 16 17 Outter p = new Outter (); 18 19 Contents c = p. getCont (); 20 21 } 22 23
7:内部类规则小结
总结一下,内部类有如下特点:
(1) :类名称只能用在定义过的范围中,除非用在限定的名称中。内部类的名称必须与
所嵌套的类不同。
(2) :内部类可以被定义在方法中。这条规则较简单,它支配到所嵌套类方法的变量的
访问。任何变量,不论是本地变量还是正式参数,如果变量被标记为 final,那么,就
可以被内部类中的方法访问。
(3) :内部类可以使用所嵌套类的类变量和实例变量以及所嵌套的块中的本地变量。
(4) :内部类可以被定义为abstract。
(5) :只有内部类可以被声明为 private 或 protected,以便防护它们不受来自外部类的
访问。访问保护不阻止内部类使用其它类的任何成员,只要一个类嵌套另一个。
(6) :一个内部类可以作为一个接口,由另一个内部类实现。
(7) :被自动地声明为static的内部类成为顶层类。这些内部类失去了在本地范围和其
它内部类中使用数据或变量的能力。
(8) :内部类不能声明任何 static 成员;只有顶层类可以声明 static 成员。因此,一个
需求static成员的内部类必须使用来自顶层类的成员。
七:再谈 Java 内存分配
Java 程序运行时的内存结构分成:方法区、栈内存、堆内存、本地方法栈几种。
栈和堆都是数据结构的知识,如果不清楚,没有关系,就当成一个不同的名字就好了,
下面的讲解不需要用到它们具体的知识。
1:方法区
方法区存放装载的类数据信息包括:
(1):基本信息:
1)每个类的全限定名
2)每个类的直接超类的全限定名(可约束类型转换)
3)该类是类还是接口
4)该类型的访问修饰符
5)直接超接口的全限定名的有序列表
(2):每个已装载类的详细信息:
1)运行时常量池:
存放该类型所用的一切常量(直接常量和对其它类型、字段、方法的符
号引用),它们以数组形式通过索引被访问,是外部调用与类联系及类型对
象化的桥梁。它是类文件(字节码)常量池的运行时表示。(还有一种静态常
量池,在字节码文件中)。
2)字段信息:
类中声明的每一个字段的信息(名,类型,修饰符)。
3)方法信息:
类中声明的每一个方法的信息(名,返回类型,参数类型,修饰符,方
法的字节码和异常表)。
4)静态变量
5)到类classloader的引用:即到该类的类装载器的引用。
6)到类class 的引用:
虚拟机为每一个被装载的类型创建一个class 实例, 用来代表这个被装
载的类。
2:栈内存
Java栈内存以帧的形式存放本地方法的调用状态(包括方法调用的参数,局部变量,中
间结果等)。每调用一个方法就将对应该方法的方法帧压入 Java 栈,成为当前方法帧。当调
用结束(返回)时,就弹出该帧。
编译器将源代码编译成字节码(.class)时,就已经将各种类型的方法的局部变量,操作
数栈大小确定并放在字节码中,随着类一并装载入方法区。当调用方法时,通过访问方法区
中的类的信息,得到局部变量以及操作数栈的大小。
也就是说:在方法中定义的一些基本类型的变量和对象的引用变量都在方法的栈内存中
分配。 当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过
变量的作用域后, Java 会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另
作它用。
栈内存的构成:
Java栈内存由局部变量区、操作数栈、帧数据区组成。
(1):局部变量区为一个以字为单位的数组,每个数组元素对应一个局部变量的值。
调用方法时,将方法的局部变量组成一个数组,通过索引来访问。若为非静态方法,则加入
一个隐含的引用参数this,该参数指向调用这个方法的对象。而静态方法则没有this参数。
因此,对象无法调用静态方法。
(2):操作数栈也是一个数组,但是通过栈操作来访问。所谓操作数是那些被指令操
作的数据。当需要对参数操作时如 a=b+c,就将即将被操作的参数压栈,如将 b 和 c 压栈,
然后由操作指令将它们弹出,并执行操作。虚拟机将操作数栈作为工作区。
(3):帧数据区处理常量池解析,异常处理等
3:堆内存
堆内存用来存放由 new创建的对象和数组。在堆中分配的内存,由 Java 虚拟机的自动
垃圾回收器来管理。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量
的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变
量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用
变量来访问堆中的数组或对象。
栈内存和堆内存比较
栈与堆都是 Java 用来在内存中存放数据的地方。与 C++不同,Java 自动管理栈和堆,
程序员不能直接地设置栈或堆。
Java 的堆是一个运行时数据区,对象从中分配空间。堆的优势是可以动态地分配内存
大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java 的垃圾收
集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度
较慢。
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈
中的数据大小与生存期必须是确定的, 缺乏灵活性。 栈中主要存放一些基本类型的变量 (int,
short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理 int a = 3;首先它会在栈中创建一个变量为 a 的引用,然后查找栈中是
否有3这个值,如果没找到,就将3存放进来,然后将 a 指向 3。接着处理int b = 3;在
创建完b 的引用变量后,因为在栈中已经有 3这个值,便将 b 直接指向 3。这样,就出现了
a与b同时均指向3的情况。内存示意图如下:
栈内存:

这时,如果再令 a=4;那么编译器 会重新搜索栈中是否有 4 值,如果没有,则将 4 存
放进来,并令 a指向4;如果已经有了,则直接将 a指向这个地址。因此 a 值的改变不会影
响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同
的,因为这种情况a的修改并不会影响到 b, 它是由编译器完成的,它有利于节省空间。此
时的内存分配示意图如下:
栈内存:

而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
4:本地方法栈内存
与调用的本地方法的语言相关,如调用的是一个 c语言方法则为一个c 栈。本地方法可
以回调java方法。若有java方法调用本地方法,虚拟机就运行这个本地方法。
在虚拟机看来运行这个本地方法就是执行这个 java 方法,如果本地方法抛出异常,虚
拟机就认为是这个java方法抛出异常。
Java通过Java本地接口JNI(Java Native Interface)来调用其它语言编写的程序,
在Java里面用native修饰符来描述一个方法是本地方法。这个了解一下就好了,在我们的
课程中不会涉及到。
5:String的内存分配
String 是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次
就会创建一个新的对象。
变量a
3
变量b
变量a
3
变量b
4
而第二种是先在栈中创建一个对 String 类的对象引用变量 str,然后查找栈中有没有
存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则
直接令str指向“abc”。
比较类里面的数值是否相等时,用 equals()方法;当测试两个包装类的引用是否指向
同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1 和str2 是指向同一个对象的。
String str1 = new String ("abc");
String str2 = new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
因此用第一种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已。这
种写法有利于节省内存空间。同时它可以在一定程度上提高程序的运行速度,因为 JVM
会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于 String str = new
String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否
有必要创建新对象,从而加重了程序的负担。
另一方面, 要注意: 我们在使用诸如 String str = "abc";的格式时,总是想当然
地认为,创建了 String 类的对象 str。担心陷阱!对象可能并没有被创建!而可能只是
指向一个先前已经创建的对象。只有通过 new()方法才能保证每次都创建一个新的对象。
由于String类的值不可变性(immutable) ,当String 变量需要经常变换其值时,应
该考虑使用StringBuffer 或StringBuilder 类,以提高程序效率。
作业
1:创建一个构造方法重载的类,并用另一个类调用
2:创建Rodent(啮齿动物) :Mouse(老鼠) ,Gerbil(鼹鼠) ,Hamster(大颊鼠)等的一个
继承分级结构。在基础类中,提供适用于所有 Rodent 的方法,并在衍生类中覆盖它们,从
而根据不同类型的 Rodent 采取不同的行动。创建一个 Rodent 数组,在其中填充不同类型
的Rodent,然后调用自己的基础类方法,看看会有什么情况发生。
3:编写MyPoint的一个子类MyXYZ,表示三维坐标点,重写 toString方法用来显示这个对
象的x、y、z 的值,如显示(1,2,3) ,最后用 main方法测试
4:当你试图编译和执行下面的程序时会发生什么?
class Mystery{
String s;
public static void main(String[] args){
Mystery m=new Mystery();
m.go();
}
void Mystery(){
s="constructor";
}
void go(){
System.out.println(s);
}
}
选择下面的正确答案:
A 编译不通过
B 编译通过但运行时产生异常
C 代码运行但屏幕上看不到任何东西
D 代码运行,屏幕上看到 constructor
E 代码运行,屏幕上看到 null
5:当编译和运行下列程序段时,会发生什么?
class Person {}
class Woman extends Person {}
class Man extends Person {}
public class Test
{
public static void main(String argv[]){
Man m=new Man();
Woman w=(Woman) new Man();
}
}
A 通过编译和并正常运行。
B 编译时出现例外。
C 编译通过,运行时出现例外。
D 编译不通过
6:对于下列代码:
1 class Person {
2 public void printValue(int i, int j) {//... }
3 public void printValue(int i){//... }
4 }
5 public class Teacher extends Person {
6 public void printValue() {//... }
7 public void printValue(int i) {//...}
8 public static void main(String args[]){
9 Person t = new Teacher();
10 t.printValue(10);
11 }
12 }
第10行语句将调用哪行语句?
A line 2
B line 3
C line 6
D line 7
7:下列代码运行结果是什么?
public class Bool{
static boolean b;
public static void main(String []args){
int x=0;
if(b){
x=1;
}
else if(b=false){
x=2;
}
else if(b){
x=3;
}
else{
x=4;
}
System.out.println(“x= ”+x);
}
}
8:在命令行输入java X Y 的结果是:
public class X{
public void main(String []args){
System.out.println(“Hello ”+args[0]);
}
}
A. Hello X Y
B. Hello X
C. Hello Y
D. 不能编译
E. 运行时有异常
9:下列代码编译并运行的结果是:
public class Test{
public static void main(String []args){
class T1 extends java.lang.Thread{}
class T2 extends T1{}
class T3 implements java.lang.Runnable{}
new T1().start();
new T2().start();
new Thread(new T3()).start();
System.out.println(“Executing”);
}
}
A. 编译失败
B. 程序能运行但永远永不终止
C. 能运行并输出“Executing”
D. 运行时有异常
10:代码运行结果是什么?
public class Test{
public static void main(String []args){
double num=7.4;
int a=(int)Math.abs(num+0.5);
int b=(int)Math.ceil(num+0.5);
int c=(int)Math.floor(num+0.5);
int d=(int)Math.round(num+0.5);
int e=(int)Math.round(num-0.5);
int f=(int)Math.floor(num-0.5);
int g=(int)Math.ceil(num-0.5);
int h=(int)Math.abs(num-0.5);
System.out.println("a="+a);
System.out.println("b="+b);
System.out.println("c="+c);
System.out.println("d="+d);
System.out.println("e="+e);
System.out.println("f="+f);
System.out.println("g="+g);
System.out.println("h="+h);
}
}
11:完成此段代码可以分别添加哪两个选项?
1. public class Test{
2.
3. public static void main(String []args){
4.
5. System.out.println(“c=”+c);
6. }
}
a) 在第2行加上语句static char c;
b) 在第2行加上语句char c;
c) 在第4行加上语句static char c;
d) 在第4行加上语句char c=’f’;
12:下列代码运行结果是什么?
public class A{
puvlic static void main(Stirng []args){
int m=2;
int p=1;
int t=0;
for(;p<5;p++){
if(t++>m){
m=p+t;
}
}
System.out.println(“t equals ”+t);
}
}
A. 2
B. 4
C. 6
D. 7
13:已知如下的命令执行 java MyTest a b c
请问哪个是正确的?
A、args[0] = "MyTest a b c"
B、args[0] = "MyTest"
C、args[0] = "a"
D、args[1]= 'b'
14:将下面类中的变量和方法改为静态的, 使程序能正确编译执行。如果保持用实例变量
和方法就必须创建对象,请创建 A的对象并通过该对象来引用实例变量和方法。
public class A
{
int a=9;
public void show(int a)
{
System.out.println(a*10);
}
public static void main(String args[]){
a+=a;
show(a);
}
}
15:设计个Circle类,其属性为圆心点(类型为前面设计的类 MyPoint)和半径,并为此类
编写以下三个方法:
一是计算圆面积的calArea()方法;
二是计算周长的calLength();
三是 boolean inCircle(MyPoint mp)方法,功能是测试作为参数的某个点是否在当前对象
圆内(圆内,包括圆上返回true;在圆外,返回 false) 。

浙公网安备 33010602011771号