7.1 类的封装
封装是面向对象编程的核心思想,将对象的属性和行为封装起来。其载体就是类。
无封装代码如下:
public class Restaurant1 {//创建一个类
public static void main(String[] args) {//主方法
String cookName="Tom Cruise";//厨师的名字叫Tom Cruise
System.out.println("**请让厨师为我做一份香辣肉丝。***");//输出
System.out.println(cookName+"切葱花");//输出
System.out.println(cookName+"洗蔬菜");//输出
System.out.println(cookName+"开始烹饪"+"香辣肉丝");//输出
System.out.println("**请问厨师叫什么名字?***");//输出
System.out.println(cookName);//输出
System.out.println("请让厨师给我切一点葱花。***");//输出
System.out.println(cookName+"切葱花");//输出
}
}
运行结果为:
封装成类代码如下:
public class Restaurant2 {//创建一个类
public static void main(String[] args) {//主方法
Cook1 cook=new Cook1();//创建厨师类的对象
System.out.println("**请让厨师为我做一份香辣肉丝。***");//输出
cook.cooking("香辣肉丝");//厨师烹饪香辣肉丝
System.out.println("**你们的厨师叫什么名字?***");//输出
System.out.println(cook.name);//厨师回答自己的名字
System.out.println("**请让厨师给我切一点葱花。***");//输出
cook.cutOnion();//厨师去切葱花
}
}
class Cook1{//创建类
String name;//厨师的名字
public Cook1(){//定义一个方法
this.name="Tom Cruise";//厨师的名字叫Tom Cruise
}
void cutOnion(){//厨师切葱花
System.out.println(name+"切葱花");//输出信息
}
void washVegetables(){//厨师洗蔬菜
System.out.println(name+"洗蔬菜");//输出信息
}
void cooking(String dish){//厨师烹饪顾客点的菜
washVegetables();//调用洗蔬菜方法
cutOnion();//调用切葱花方法
System.out.println(name+"开始烹饪"+dish);//输出信息
}
}
运行结果为:
使用private修饰代码如下:
public class Restaurant4 {//创建类
private Cook2 cook=new Cook2();//餐厅封装的厨师类
public void takeOlder(String dish){//下单 Restaurant2 a=new Restaurant2();//创建一个对象
cook.cooking(dish);//通知厨师做菜
System.out.println("您的菜好了,请慢用。");//输出信息
}
public String saySorry(){//拒绝顾客请求
return"抱歉,餐厅不提供此项服务。";//返回字符串
}
public static void main(String[] args) {//主方法
Restaurant4 water=new Restaurant4();//创建餐厅对象,为顾客提供服务
System.out.println("**请让厨师为我做一份香辣肉丝。***");//输出
water.takeOlder("香辣肉丝");//服务员给顾客下单
System.out.println("**你们的厨师叫什么名字?***");//输出信息
System.out.println(water.saySorry());//服务员给顾客善意的答复
System.out.println("**请让厨师给我切一点葱花。***");//输出信息
System.out.println(water.saySorry());//服务员给顾客善意的答复
}
}
运行结果为:
7.2类的继承
继承在面向对象开发思想中是一个非常重要的概念,它使整个程序架构具有一定的弹性,在程序中复用已经定义完善的类不仅可以减少软件开发周期,还可以提高软件的可维护性和可扩展性。其基本思想是基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。例如,平行四边形是特殊的四边形,可以说平行四边形类继承了四边形类,这时平行四边形类将所有四边形具有的属性和方法都保留下来,并基于四边形类扩展了一些新的平行四边形类特有的属性和方法。
7.2.1extends 关键字
让一个类继承另一个类,用extends关键字,语法如下:
child extends parents
代码如下:
class Computer{//父类
String screen="液晶显示屏";
void startup(){//开机方法
System.out.println("电脑正在开机,请等待...");//输出
}
}
public class Pad extends Computer{//创建一个类并继承父类
String battery="5000毫安电池";//子类独有的属性
public static void main(String[] args) {//主方法
Computer pc=new Computer();//电脑类
System.out.println("computer的屏幕是:"+pc.screen);//输出
pc.startup();//调用父类方法
Pad ipad=new Pad();//平板电脑类
System.out.println("pad的屏幕是:"+ipad.screen);//子类可以直接使用父类属性
System.out.println("pad的屏幕是:"+ipad.battery);//子类独有的属性
ipad.startup();//子类可以直接使用父类方法
}
}
运行结果为:
7.2.2方法的重写
1.重写的实现
继承并不只是扩展父类的功能,还可以重写父类的成员方法。重写(还可以称为覆盖)就是类中将父类的成员方法的名称保留,重新编写成员方法的实现内容,更改成员方法的存储权限,可修改成员方法的返回值类型。
代码如下:
class Computer2{//父类:电脑
void showPicture(){//定义一个方法
System.out.println("鼠标单击");//输出信息
}
}
public class Pad2 extends Computer2 {//创建一个类并继承父类
void showPicture(){//重写父类的方法
System.out.println("手指点击触摸屏");//输出信息
}
public static void main(String[] args) {//主方法
Computer2 pc=new Computer2();//电脑类
System.out.print("pc打开图片:");//输出信息
pc.showPicture();//调用方法
Pad2 ipad=new Pad2();//平板电脑类
System.out.print("ipad打开图片:");//输出信息
ipad.showPicture();//重写父类方法
Computer2 computerpad=new Pad2();//父类声明,子类实现
System.out.print("computerpad打开图片:");//输出信息
computerpad.showPicture();//调用父类方法,实现子类重写的逻辑
}
}
运行结果为:
2. super关键字
如果想在子类的方法中实现父类原有的方法 解决这种需求,Java 提供了 super 关键字。
super 关键字的使用方法与 this 关键字类似。this 关键字代表本类对象,super关键字代表文对象,使用方法如下:
super.property; //调用父类的属性 super.method();//调用父类的方法
代码如下:
class Computer3{//父类:电脑
String sayHello(){//定义一个方法
return"欢迎使用";//返回一个字符串
}
}
public class Pad3 extends Computer3 {//子类:平板电脑
String sayHello(){//重写方法
return super.sayHello()+"平板电脑";//调用父类方法,在其结果后添加字符串
}
public static void main(String[] args) {//主方法
Computer3 pc=new Computer3();//电脑类
System.out.println(pc.sayHello());//调用父类的方法并输出
Pad3 ipad=new Pad3();//平板电脑类
System.out.println(ipad.sayHello());//调用子类的方法并输出
}
}
运行结果为:
7.2.3所有类的父类--Object类
使用 class关键字定义类时,就应用了继承原理,因或间接继承了java.lang.Object类。Object类是比较特殊的类,它是所有类的父类,是 Java 类层中的最高层类。当创建一个类 class时,总是在继承,除非某个类已经指定要从其他类继承,否则它就是从java.lang.Object类继承而来的,可见Java中的每个类都源于java.lang.Object类,如String、Integer等类都是继承于Object类;除此之外自定义的类也都继承于Object类。由于所有class类都是Object子类,所以在定义类时,省略了 extends Object关键字,在Object类中主要包括cloneO、finalizeequals、toString0等方法,其中常用的两个方法为equals()和toString0方法。由于所有的类都是Object类的子类,所以任何类都可以重写 Object类中的方法。
1.getClass()方法
getClass()方法是Object类定义的方法,它会返回对象执行时的 Class实例,然后使用此例用getName()方法可以取得类的名称。
语法如下:
getclass().getName();
可以将getClass()方法与toString()方法联合使用。
2. toString()方法
toString0方法的功能是将一个对象返回为字符串形式,它会返回一个 String实例。在实际的营用中通常重写toString0)方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符甲连接时,将自动调用重写的 toString()方法。
代码如下:
public class Can {//创建类
public String toString(){//重写toString()方法
return "在"+getClass().getName()+"类中重写toString()方法";//返回值
}
public static void main(String[] args) {//主方法
System.out.println(new Can());//调用方法并输出,打印本类对象
}
}
运行结果为:
3. equals()方法
equals()方法,是比较“==”运算符与equals()方法,说明”==”比是两个对象的引用是否相等,而equals0方法比较的是两个对象的实际内容。
代码如下:
public class Can {//创建类
public static void main(String[] args) {//主方法
String s1="123";//实例化两个对象,内容相同
String s2="123";//实例化对象
System.out.println(s1.equals(s2));//使用equals()方法
Can v1= new Can();//实例化两个类对象
Can v2=new Can();//实例化对象
System.out.println(v1.equals(v2));//使用equals()方法
}
}
运行结果为:
7.3类的多态
多态意为一个名字可具有多种语义,在程序设计语言中,多态性是指“一种定义,多种实现”例如,运算符“+”作用于两个整型量时是求和,而作用于两个字符型量时则是将其连接在一起。用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。类的多态性可以从两方面体现:一是方法的重载,二是类的上下转型
7.3.1 方法的重载
我们知道构造方法的名称由类名决定,所以构造方法只有一个名称,但如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到“方法重载”虽然方法重载起源于构造方法,但是它也可以应用到其他方法中。
方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同就可。
代码如下:
public class Can {//创建类
public static int add(int a){//定义一个方法并传入一个int型参数
return a;//返回一个整型值
}
public static int add(int a,int b){//定义一个方法并传入两个参数
return a+b;//返回a+b的值
}
public static double add(double a,double b){//定义一个方法并传入两个参数
return a+b;//返回a+b的值
}
public static int add(int a,double b){//定义一个方法并传入两个参数
return (int)(a+b);//返回a+b的值,同时利用强制转换成int型
}
public static int add(double a,int b){//定义一个方法并传入两个参数
return (int)(a+b);//返回a+b的值,同时利用强制转换成int型
}
public static int add(int...a){//定义一个不定长参数方法
int s=0;//定义一个int型变量并赋予初值
for(int i=0;i
运行结果为:
7.3.2 向上转型
子类转变成父类类型
代码如下:
class Quadrangle{//四边形类
public static void draw(Quadrangle q){//四边形类中的方法
//SomeSentence
}
}
public class Parallelogram extends Quadrangle{//平行四边形类,继承了四边形类
public static void main(String args[]){//主方法
Parallelogram p=new Parallelogram();//实例化平行四边形类对象引用
draw(p);//调用父类方法
}
}
7.3.3 向下转型
父类转变成子类 使用强制转换
代码如下:
class Quadrangle{//父类
public static void draw(Quadrangle q){//父类方法
//SomeSentence
}
}
public class Parallelogram extends Quadrangle {//子类继承父类
public static void main(String[] args) {//主方法
draw(new Parallelogram());//调用方法并传入子类对象
Quadrangle q=new Parallelogram();//将平行四边形对象看作是四边形对象,称为向上转型操作
Parallelogram p=q;//向下转型
}
}
运行结果为:
7.3.4 instanceof关键字
当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生CastException 异常,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是否子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是一个类实现了某个接口(接口会在74节中进行介绍),也可以用它来判断一个实例对象是否属于个类。
instanceof的语法格式如下:
myobject instanceof ExampleClass
myobject:某类的对象引用。
ExampleClass:某个类。
使用instanceof操作符的表达式返回值为布尔值。如果返回值为true,说明myobject 对象ExampleClass 的实例对象;如果返回的值为false,说明myobject对象不是ExampleClass的实例对象。
代码如下:
class Quadrangle{//类名
public static void draw(Quadrangle q){//构造一个方法并传入一个参数
//SomeSentence
}
}
class Square extends Quadrangle{//构造一个类并继承另一个类
//SomeSentence
}
class Anything{//构造一个类
//SomeSentence
}
public class Parallelogram extends Quadrangle {//创建一个类并继承另一个类
public static void main(String[] args) {//主方法
Quadrangle q=new Quadrangle();//创建一个对象
if(q instanceof Parallelogram){//判断该对象是否属于该类
Parallelogram p=(Parallelogram)q;//进行强制转换
}
if(q instanceof Square){//判断该对象是否属于该类
Square s=(Square) q;//进行强制转换
}
System.out.println(q instanceof Anything );//输出信息
}
}
运行结果为:
7.3 抽象类与接口
7.4.1抽象类与抽象方法
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回提继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所以在 Java 语言中设置抽象不可以实例化对象,因为图形类不能抽象出任何一种具体图形,但它的子类却可以。
Java中定义抽象类时,需要使用abstract关键字,其语法如下:
[权限修饰符] abstract class 类名 {
类体
}
使用abstract关键字定义的类称为抽象类,而使用abstract关键字定义的方法称为抽象方法,抽象方法的定义语法如下:
[权限修饰符] abstract 方法返回值类型 方法名 (参数列表);
public abstract class Market {//创建类
public String name;//商场名称
public String goods;//商品名称
public abstract void shop();//抽象方法,用来输出信息
}
public class TaobaoMarket extends Market {//创建类并继承商场类
@Override
public void shop() {//重写方法
// TODO Auto-generated method stub
System.out.println(name+"网购"+goods); //输出信息
}
}
public class GoShopping {//类名
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
Market market=new WallMarket();//向上转型
market.name="沃尔玛";//赋值
market.goods="七匹狼西服";//赋值
market.shop();//调用方法
market=new TaobaoMarket();//创建一个对象
market.name="淘宝";//赋值
market.goods="韩都衣舍花裙";//赋值
market.shop();//调用方法
}
}
运行结果为:
综上所述,使用抽象类和抽象方法时,需要遵循以 下原则:
(1)在抽象类中,可以包含抽象方法,也可以不包已含抽象方法,但是包含了抽象方法的类必须被定义为抽象类。
(2)抽象类不能直接实例化,即使抽象类中没有声 明抽象方法,也不能实例化。
(3)抽象类被继承后,子类需要实现其中所有的抽象方法。
(4)如果继承抽象类的子类也被声明为抽象类,则可以不用实现父类中所有的抽象方法。使用抽象类时,可能会出现这样的问题:程序中会有太多冗余的代码,同时这样的父类局限生很大,例如,上面的例子中,也许某个不需要 shop()方法的子类也必须重写shopO方法。如果将这个shopO方法从父类中拿出,放在别的类里,又会出现新问题,就是某些类想要实现“买衣服”的场景,竟然需要继承两个父类。Java 中规定,类不能同时继承多个父类,面临这种问题时,接口的概念便出现了。
7.4.2接口的声明及实现
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。
接口使用interface 关键字进行定义,语法如下:
[ 修饰符 ] interface 接口名 [ extends 父接口名列表 ] {
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
修饰符:可选,用于指定接口的访问权限,可选值为 public。如果省略则使用默认的访权限。
接口名:必选参数,用于指定接口的名称,接口名必须是合法的 Java标识符。一般情况下要求首字母大写。
extends父接口名列表:可选参数,用于指定要定义的接口继承
于哪个父接口。当使用extends关键字时,父接口名为必选参数。方法:接口方法定义而没有被实现。
一个类实现一个接口可以使用implements关键字,代码如下:
public class Parallelogram extends Quadrangle implements drawTest{
…//
}
代码如下:
interface drawTest{//定义接口
public void draw();//定义方法
}
class ParallelogramgleUseInterface implements drawTest{//创建类并继承接口
@Override
public void draw() {//重写方法
// TODO Auto-generated method stub
System.out.println("平行四边形.draw()");//输出
}
}
class SquareUseInterface implements drawTest{//创建类并继承接口
@Override
public void draw() {//重写方法
// TODO Auto-generated method stub
System.out.println("正方形.draw()");//输出
}
}
public class QuadrangleUseInterface {//创建类
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
drawTest[]d={//定义一个一维数组
new SquareUseInterface(),new ParallelogramgleUseInterface()};//向上转型
for(int i=0;i
运行结果为:
7.4.3多重继承
在Java许继承但使用口可以实现多重继承,因为一个类可以同时实现多接口,这样可以将所有需要实现的接口放置在iplents字后并使用逗号”,“隔开,但会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。
通过接口实现多重继承的语法如下:
class 类名 implements 接口1,接口2,…,接口n
代码如下:
public interface IFather {//定义一个接口
void smoking();//抽烟的方法
void goFishing();//钓鱼方法
}
public interface IMother {//定义一个接口
void watchTV();//看电视的方法
void cooking();//做饭的方法
}
public class Me implements IFather,IMother {//创建类并继承两个接口
public void watchTV() {//重写方法
// TODO Auto-generated method stub
System.out.println("我喜欢看电视");//输出信息
}
public void cooking() {//重写方法
// TODO Auto-generated method stub
System.out.println("我喜欢做饭");//输出信息
}
public void smoking() {//重写方法
// TODO Auto-generated method stub
System.out.println("我喜欢抽烟");//输出信息
}
@Override
public void goFishing() {//重写方法
// TODO Auto-generated method stub
System.out.println("我喜欢钓鱼");//输出信息
}
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
IFather father=new Me();//进行向上转型
System.out.println("爸爸的爱好:");//输出信息
father.smoking();//调用方法
father.goFishing();//调用方法
IMother mather=new Me();//进行向上转型
System.out.println("妈妈的爱好:");//输出信息
mather.cooking();//调用方法
mather.watchTV();//调用方法
}
}
运行结果为:
7.4.4区分抽象类与接口
抽象类和接口都包含可以由子类继承实现的成员,但抽象类是对根源的抽象,而接口是对动作的抽象。抽象类的功能要远超过接口,那为什么还要使用接口呢?这主要是由于定义抽象类的代价高(因为每个类只能继承一个类,在这个类中,必须继承或编写出其子类的所有共性,因此,虽然接口在功能上会弱化许多,但它只是针对一个动作的描述,而且可以在一个类中同时实现多个接口,这样会降低设计阶段的难度。
抽象类和接口的区别主要有以下几点。
注意:
(1)子类只能继承一个抽象类,但可以实现任意多个接口。
声明类时,如果不使用p
(2)一个类要实现一个接口必须实现接口中的所有方法,而抽象类不必。
(3)抽象类中的成员变量可以是各种类型,而接口中的成员变量只能是public static finalf
口多学两招:
Java语言中,类的权限
(4)接口中只能定义抽象方法,而抽象类中可以定义非抽象方法。
定义一个public的doSt
(5)抽象类中可以有静态方法和静态代码块等,接口中不可以。
下面两段代码是等效的
(6)接口不能被实例化,没有构造方法,但抽象类可以有构造方法。
7.5 访问控制
7.5.1 访问控制符
前面介绍了面向对象的几个基本特性,其中包括封装性,封装实际上有两方面的含义:把谈藏的隐藏起来、把该暴露的暴露出来,这两个方面都需要通过使用Java提供的“访问控制符”来现,Java中的访问控制符主要包括public、protected、private和default(缺省)等4种,这些控育符控制着类和类的成员变量以及成员方法的访问权限。
使用访问控制符时,需要遵循以下原则。
(1)大部分顶级类都使用public修饰;
(2)如果某个类主要用作其他类的父类,该类中包含的大部分方法只是希望被其子类重写。而不想被外界直接调用,则应该使用protected修饰;
(3)类中的绝大部分属性都应该使用private修饰,除非一些static或者类似全局变量的属性,才考虑使用public 修饰;
(4)当定义的方法只是用于辅助实现该类的其他方法(即工具方法),应该使用private修饰;(5)希望允许其他类自由调用的方法应该使用public修饰。
7.5.2 Java 类包
在Java中每定义好一个类,通过Java编译器进行编译之后,都会生成一个扩展名为class的文件,当这个程序的规模逐渐庞大时,就很容易发生类名称冲突的现象。那么JDK API中提供了成千上万具有各种功能的类,Java中提供了一种管理类文件的机制,就是类包。Java中每个接口或类都来自不同的类包,无论是JavaAPI中的类与接口还是自定义的类与接口都需要隶属于某一个类包,这个类包包含了一些类和接口。如果没有包的存在,管理程序中的类名称将是一件非常麻烦的事情,如果程序只由一个类定义组成,并不会给程序带来什么影响,但是险着程序代码的增多,难免会出现类同名的问题。例如,在程序中定义一个Login类,因业务需要,还要定义一个名称为Login 的类,但是这两个类所实现的功能完全不同,于是问题就产生了,编译器不会允许存在同名的类文件。解决这类问题的办法是将这两个类放置在不同的类包中,实际上 ava中类的完整名称是包名与类名的组合。
在类中定义包名的语法如下:
package包名1[.包名2[.包名3...]];
在上面的语法中。包名可以设置多个,包名和包名之间使用.分割,包名的个数没有限制,其中前面的包名包含后面的包名。
在类中指定包名时需要将package放置在程序的第一行,它必须是文件中的第一行非注释代码当使用package关键字为类指定包名之后,包名会成为类名中的一部分,预示着这个类必须指定全名。
7.5.3 final 关键字
1.final 类
定义为final的类不能被继承。如果希望一个类不允许任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final形式。
final类的语法如下:
final class类名()
如果将某个类设置为final形式,则类中的所有方法都被隐式地设置为final形式,但是final类中的成员变量可以被定义为final或非final形式。
代码如下:
final class FinalClass {//创建类
int a=3;//定义一个整型变量并赋予初值
void doit(){//定义一个方法
}
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
FinalClass f=new FinalClass();//创建对象
f.a++;//调用成员变量并进行自增运算
System.out.println(f.a);//输出
}
}
2.final方法
定义的final的方法不能被重写,在父类中定义为private final 可以被子类覆盖。
代码如下:
class Parents{//类名
private final void doit(){//方法
System.out.println("父类.doit()");//输出
}
final void doit2(){//方法
System.out.println("父类.doit2()");//输出
}
public void doit3(){//方法
System.out.println("父类.doit3()");//输出
}
}
class Sub extends Parents{//创建类并继承父类
public final void doit(){//方法
System.out.println("子类.doit()");//输出
}
// final void doit2(){//方法
// System.out.println("父类.doit2()");
//}
public void doit3(){//方法
System.out.println("子类.doit3()");//输出
}
}
public class FinalMethod {//创建类
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
Sub s=new Sub();//创建对象
s.doit();//调用方法
Parents p=s;//执行向上转型操作
//p.doit();//不能调用private方法
p.doit2();//调用方法
p.doit3();//调用方法
}
}
运行结果为:
3.final 变量
f inal关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。例如,在类中定义PI值,可以使用如下语句:
final double PI=3.14;
当在程序中使用PI这个常量时,它的值就是3.14,如果在程序中再次对定义为 final的常量赋值,编译器将不会接受。
final关键字定义的变量必须在声明时对其进行赋值操作。final除了可以修饰基本数据类型的常量,还可以修饰对象引用。由于数组也可以被看作一个对象来引用,所以final可以修饰数组。一旦一个对象引用被修饰为final 后,它只能恒定指向一个对象,无法将其改变以指向另一个对象。一个是static 又是 final 的字段只占据一段不能改变的存储空间。
代码如下:
import java.util.Random;//导入random方法
import static java.lang.System.out;//导入方法
class Test{//类名
int i=0;//定义一个整型变量并初始化
}
public class FinalData {//创建类
static Random rand=new Random();//创建对象
private final int VALUE_1=9;//定义常量
private static final int VALUE_2=10;//定义常量
private final Test test=new Test();//创建对象
private Test test2=new Test();//创建对象
private final int[]a={1,2,3,4,5,6};//定义一个一维数组并赋值
private final int i4=rand.nextInt(20);//定义变量并随即赋值
private static final int i5=rand.nextInt(20);//定义变量随机赋值
public String toString(){//使用toString()方法
return i4+" "+i5+" ";//返回字符串
}
public static void main(String[] args) {//主方法
FinalData data=new FinalData();//创建对象
data.test=new Test();//创建对象
data.VALUE_2;//调用变量
data.test2=new Test();//向上转型
for(int i=0;i
运行结果为:
import java.util.Random;//导入Random方法
import static java.lang.System.out;//导入方法
public class FinalStaticData {//创建类
private static Random rand=new Random();//实例化一个Random类对象
private final int a1=rand.nextInt(10);//随机赋值
private static final int a2=rand.nextInt(10);//随机赋值
public static void main(String[] args) {//主方法
FinalStaticData fdata=new FinalStaticData();//创建对象
out.println("重新实例化对象调用a1的值:"+fdata.a1);//输出
out.println("重新实例化对象调用a1的值:"+fdata.a2);//输出
FinalStaticData fdata2=new FinalStaticData();//创建对象
out.println("重新实例化对象调用a1的值:"+fdata2.a1);//输出
out.println("重新实例化对象调用a2的值:"+fdata2.a2);//输出
}
}
运行结果为:
从本实例的运行结果中可以看出,定义为 final的常量不是恒定不变的,将随机数赋予定义为 final的常量,可以做到每次运行程序时改变al的值。但是a2与al不同,由于它被声明为 static final形式,所以在内存中为a2开辟了一个恒定不变的区域,当再次实例化一个FinalStaticData对象时,仍然指向a2这块内存区域,所以a2的值保持不变。a2 是在三装载时被初始化,并不是每次创建新对象时都被初始化,而al会在重新实例化对象时被更改。
7.6内部类
7.6.1 成员内部类
在一个类使用内部类,可以在内部类中直接存取其所在类的私有成员变量。
语法如下:
public class OuterClass{ //外部类
private class InnerClass { //内部类
//...
}
}
在内部类中可以随意使用外部类的成员方法以及成员变量,尽管这些成员被修饰为private。
内部类的实例一定要绑定在外部类的实例上,如果从外部类中初始化一个内部类对象。那么内部类对象就会绑定在外部类对象上。内部类初始化方式与其他初始化方式相同,都是使用new 关键字。
代码如下:
public class OuterClass {//创建类
innerClass in=new innerClass();//创建对象
public void ouf(){//方法
}
class innerClass{//类名
innerClass(){//方法
}
public void inf(){//方法
}
int y=0;//定义变量
}
public innerClass doit(){//方法
//y=4;
in.y=4;//调用变量
return new innerClass();//返回对象
}
public static void main(String[] args) {//主方法
OuterClass out=new OuterClass();//创建对象
OuterClass.innerClass in=out.doit();//调用方法
OuterClass.innerClass in2=out.new innerClass();//向上转型
}
}
2.内部类向上转型为接口:
2.内部类向上转型为接口
如果将一个权限修饰符为private的内部类向上转型为其父类对象,或者直接向上转型为一个提
口,在程序中就可以完全隐藏内部类的具体实现过程。 可以在外部提供一个接口,在接口中声明-
个方法。如果在实现该接口的内部类中实现该接口的方法,就可以定义多个内部类以不同的方式实现接口中的同一个方法,而在一般的类中是不能多次实现接口中同一个方法的,这种技巧经常被用在Swing编程中,可以在一个类中做出多个不同的响应事件。
代码如下:
interface OutInterface{//定义接口
public void f();//抽象方法
}
public class InterfaceInner {//创建类
public static void main(String[] args) {主方法
OuterClass2 out=new OuterClass2();//创建对象
OutInterface outinter=out.doit();//调用方法
outinter.f();//调用方法
}
}
class OuterClass2{//类名
private class InnerClass implements OutInterface{//继承接口
InnerClass(String s){//重写方法
System.out.println(s);//输出
}
public void f() {//重写方法
System.out.println("访问内部类中的f()方法");//输出信息
}
}
public OutInterface doit(){//重写方法
return new InnerClass("访问内部类构造方法");//输出信息
}
}
运行结果为:
3.使用this关键字获取内部类与外部类的引用。
如果在外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this关键字》
代码如下:
public class TheSameName {//类名
private int x;//定义整型变量
private class Inner{//定义方法
private int x=9;//定义整型变量并初始化
public void doit(int x){//方法
x++;//自增运算
this.x++;//自增运算
TheSameName.this.x++;//自增运算
}
}
}
在类中,如果遇到内部类与外部类的成员变量重名相同,可以使用this关键字进行处理。
使用成员内部类时,应该遵循以下原则:
(1)可以有各种修饰符,可以用 porivate、public、protected、static、final、abstract等修饰;
(2)如果内部类有static限定,就是类级别的,否则为对象级别。类级别可以通过外部类直接访问,对象级别需要先生成外部的对象后才能访问;
(3)内外部类不能同名;
(4)非静态内部类中不能声明任何 static 成员;
(5)内部类可以互相调用。
7.6.2 局部内部类
内部类不仅可以在类中进行定义,也可以在类的局部位置定义,如在类的方法或任意的作用域中均可以定义内部类。
代码如下:
interface OutInterface2{//定义接口
}
class OuterClass3 {//类名
public OutInterface2 doit(final String x){//方法
class InnerClass2 implements OutInterface2(String s){类继承
InnerClass2(String s){//内部类
s=x;//赋值
System.out.println(s);//输出
}
}
return new InnerClass2("doit");//返回值
}
}
7.6.3 匿名内部类
代码如下:
interface OutInterface2{//定义一个接口
}
class OuterClass4{//创建类
public OutInterface2 doit(){//定义方法
return new OutInterface2(){//返回值
private int i=0;//定义变量并初始化
public int getValue(){//方法
return i;//返回值
}
};
}
}
匿名类的所有实现代码都需要在大括号之间进行编写,语法如下:
return new A() {
...//内部类体
};
其中,A指类名。
由于匿名内部类没有名称,所以匿名内部类使用默认构造方法来生成Outlnterface2 对象。在匿名内部类定义结束后,需要加分号标识,这个分号并不是代表定义内部类结束的标识,而是代表创建OutInterface2引用表达式的标识。
说明:
匿名内部类编译以后,会产生以“外部类名S序号”为名称的class文件,序号以1~n排列,分别代表I~n个圈名内部类。
使用匿名内部类时应该遵循以下原则:
(1)匿名类没有构造方法;
(2)匿名类不能定义静态的成员;
(3)匿名类不能用private、public、protected、static、final、abstract等修饰;
(4)只可以创建一个匿名类实例。
7.6.4 静态内部类
在内部类前添加修饰符static,这个内部类就变为静态内部类了,一个静态内部类可以声明静态成员,但是在非静态内部类中不可以声明静态成员。静态内部类有一个最大的特点,就是不能用外部类的非静态成员,所以静态内部类在程序开发中比较少见。
可以这样认为,普通的内部类对象隐式地在外部保存了一个引用,指向创建它的外部类对象但如果内部类被定义为static,就会有更多的限制。
静态内部类具有以下两个特点:
(1)如果创建静态内部类的对象,不需要创建其外部类的对象;
(2)不能从静态内部类的对象中访问非静态外部类的对象。
代码如下:
public class StaticInnerClass {//创建类
int x=100;//定义一个整型变量并初始化
static class Inner{//内部类
void doitInner(){//方法
//System. out. printin("外部类"+x);//输出
}
public static void main(String args[]){//主方法
System. out. println();//换行
}
}
}
7.6.5 内部类的继承
内部类和其他普通类一样可以被继承,但是继承内部类比继承普通类更复杂,需要设置专门的语法来完成。
代码如下:
public class OutputInnerClass extends ClassA.ClassB{//创建类并继承另一个类
public OutputInnerClass(ClassA a){//方法
a.super();//调用父类构造方法
}
}
class ClassA{//外部类
class ClassB{//内部类
}
}



