Skip to content

Latest commit

 

History

History
361 lines (243 loc) · 13.7 KB

ch6_6.md

File metadata and controls

361 lines (243 loc) · 13.7 KB

[TOC]


#内部类和匿名类

##内部类的定义

简单地说,一个类被嵌套定义于另一个类中,称为称为内部类(Inner Classes)或嵌套类。 在大多数情况下,嵌套类(静态的嵌套类除外)就是内部类(inner class)。包含内部类的类称为外部类。与一般的类相同,内部类具有自己的成员变量和成员方法。通过建立内部类的对象,可以存取其成员变量和调用其成员方法。

例如:

pubic class GroupOne{
  int count//外部类的成员变量
  
  public class Student{     //声明内部类
     String name//内部类的成员变量
     
     public void output(){   //内部类的成员方法
        System.out.println(this.name+" ");
     }    
  }
}

实际上,Java语言规范对于内部类有如下的规定:

  • 在另一个类或者一个接口中声明一个类。
  • 在另一个接口或者一个类中声明一个接口。
  • 在一个方法中声明一个类。
  • 类和接口声明可嵌套任意深度。

内部类特性

  • Java将内部类作为外部类的一个成员,内部类可以调用包含它的外部类的成员变量和成员方法,所以不必把外部类的引用传递给内部类的构造方法。
  • 内部类的类名只能用在外部类和内部类自身中,内部类的类名不能与外部类的类名相同。当外部类引用内部类时,须给出完整的名称(外部类名.内部类名)。
  • 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。名称不能与包含它的类名相同。
  • 可以使用包含它的外部类的静态成员变量和实例成员变量,也可以使用它所在方法的局部变量。
  • 可以声明为public、private或protected,其意义与用在类的其他成员上相同。
  • 可以定义为abstract。
    • 若被声明为static,就变成了顶层类,不能再使用局部变量。
    • 若想在内部类中声明任何static成员,则该内部类必须声明为static。
    • __static__型内部类只能访问外部类中的__static__成员。
    • 若要访问非static成员,须先创建一个外部类对象,然后通过该对象进行访问。

外部类与内部类的访问原则

  • 在外部类中,一般通过一个内部类的对象来访问内部类的成员变量或方法;
  • 在内部类中,可以直接访问外部类的所有成员变量和方法(包括静态成员变量和方法、实例成员变量和方法及私有成员变量和方法)。

例如

public class GroupTwo {
	
	private int count;    //外部类的私有成员变量
   
   public class Student {    //声明内部类
   		String name;
      
       public Student(String n1) {
       	name=n1;
          count++;    //存取其外部类的成员变量
       }
       
       public void output(){
       	System.out.println(this.name);
       } 
	}
   
   public void output(){   //外部类的实例成员方法
   		Student s1=new Student("Johnson");    //建立内部类对象"
       s1.output();    //通过s1调用内部类的成员方法
       System.out.println("count="+this.count);
   }
   
   public static void main(String args[]){
   		GroupTwo g2=new GroupTwo();
       g2.output();
   }
}

本例的类GroupTwo中声明了成员变量count、内部类Student、实例方法output和main方法,在内部类Student中声明了构造方法和output方法,构造方法存取了外部类GroupTwo的成员变量count。

程序运行结果:

Johnson
count=1

本例演示嵌套的两个类之间的访问规则,即在外部类GroupTwo中,通过一个内部类Student的对象s1可以引用内部类中的成员;反之,在内部类Student中可以直接引用它的外部类的成员,如count。

本例的外部类GroupTwo中有实例方法output(),内部类Student中也有实例方法output(),两者虽然同名,却表达不同含义。使用时,外部类GroupTwo的对象调用GroupTwo的output,如g2.output(),内部类Student的对象调用Student的output,如s1.output()。

##匿名类

有时在定义事件处理代码时,由于代码短小,不必再明确定义一个类,可使用匿名内部类。

  • 匿名内部类是__final__(最终)类,非static类,匿名内部类将类的声明和创建类的实例一步完成。
  • 主要应用在事件处理的代码编写中。
  • 匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。

要采用另一种形式的new语句,如下所示:

new <类或接口> <类的主体>

这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。

从技术上说,匿名类可被视为非静态的内部类,所以它们具有和方法内部声明的非静态内部类一样的权限和限制。� 内部和匿名类是Java为我们提供的两个出色的工具。它们提供了更好的封装,结果就是使代码更容易理解和维护,使相关的类都能存在于同一个源代码文件中(这要归功于内部类),并能避免一个程序产生大量非常小的类(这要归功于匿名类)。

例如:

import java.awt.* ;�import java.awt.event.*;�
public class AnonymousClass{
	private Frame f;
	private TextField tf;
	
	public AnonymousClass(){
		f=new Frame("Inner classes example");
		tf=new TextField(30);
	}

	public void launchFrame(){
		Label label=new Label("Click and drag the mouse");�
		f.add(label,BorderLayout.NORTH);
		f.add(tf,BorderLayout.SOUTH);
		
		f.addMouseMotionListener(new MouseMotionAdapter(){
			//匿名类开始
			public void mouseDragged(MouseEvent e){
				String s="Mouse dragging:  
				x="+e.getX()+"Y="+e.getY();
				tf.setText(s); 
			}�		}); //匿名类结束

		f.setSize(300,200);
		f.setVisible(true);
	}�  
	public static void main(String args[]){
		AnonymousClass obj=new AnonymousClass();
		obj.launchFrame();
	}
}�

注意:在使用匿名内部类时,要记住以下几个原则:

  • (1)匿名内部类不能有构造方法;
  • (2)匿名内部类不能定义任何静态成员、方法和类。
  • (3)匿名内部类不能是__public__, protected, private, static
  • (4)只能创建匿名内部类的一个实例。
    一个匿名内部类一定是在__new__的后面,用其隐含实现一个接口或实现一个类。
    因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
    内部类只能访问外部类的静态变量或静态方法。
  • (5)当在匿名类中用__this__时,这个__this__则指的是匿名类或内部类本身。
    这时如果要使用外部类的方法和变量,则应该加上外部类的类名

#包(package)

包(package)是Java提供的文件(即公共类)的组织方式。
一个包对应一个文件夹,一个包中可以包括许多类文件。
包中还可以再有子包,称为包等级。

##包的作用有四个:

  • (1) 定位类:具有相似功能的类可以放置在同一个包中,这样可以很容易地查找定位类。
  • (2) 避免命名冲突:在开发由其他程序员共享的可复用类时,会发生命名冲突,可以把类放在不同包中,通过包名引用类可以避免命名冲突。
  • (3) 可以方便地分发软件。
  • (4) 控制类之间的访问

注意:

  • 包是一个类名空间,同一个包中的类和接口不能重名,不同包中的类可以重名。
  • 根据Java的命名规则,包名均为小写字母。
  • 类之间的访问控制是通过类修饰符来实现的,若类修饰符为__public__,则表明该类不仅可供同一包中的类访问,也可以被其他包中的类访问。
  • 若类无修饰符,则表明该类仅供同一包中的类访问。
  • Java的包等级和Windows的文件组织方式完全相同,只是表示方法不同。

##包的定义

包的定义就是将源程序文件中的接口和类纳入指定的包。

一般情况下,Java源程序由四部分组成:

  • (1) 一个包(package)定义语句(可选项)。其作用是将本源文件中的接口和类纳入指定包。
    源文件中若有包说明语句,必须是第一个语句;
  • (2) 若干个(import)语句(可选项)。其作用是引入本源文件中所需要使用的包;
  • (3) 一个__public__的类声明。
    在一个源文件中只能有一个__public__类;
  • (4) 若干个属于本包的类声明(可选)。

包的定义语句格式:

package 包名1[.包名2[.包名3…]];

  • 创建包就是在当前文件夹下创建一个子文件夹,存放这个包中包含的所有类和接口的.class文件。
  • 语句中的符号“.”代表了目录分隔符,说明这个语句创建了两个文件夹。

==习惯上,包名都用小写字母。==

Java规定,如果一个Java文件中有package语句,那么package语句必须写在Java源程序的第一行,该行前只能有空格和注释行。
Package语句在每个Java源程序中只能有一条,一个类只能属于一个包。

例如:

package cn.edu.hebiace;

定义了包,语句中的包名分隔符“.”相当于目录分隔符。
使用package语句指定一个源文件中的类属于一个特定的包。

Java要求包名与文件系统的目录结构一一对应。
对于名为cn.edu.hebiace的包,必须创建一个如图所示的目录结构。

若源文件中未使用package语句创建包,则该源文件中的接口和类位于Java的无名包中(无名包又称默认包,指当前目录),会把源文件中的类存储在当前目录(即存放Java源文件的目录)下。

==无名包中的类不能被其他包中的类引用和复用==

##设置类路径

包是一种组织代码的有效手段,包名指出了程序中需要使用的".class"文件的所在之处。
另一个能指明".class"文件所在的位置是环境变量CLASSPATH。

对于Java Application程序,还可以通过为Java解释器设置参数来指定类文件路径。

例如, 对于JDK中的Java解释器java.exe,有开关参数-classpath;

假设当需要解释执行的test.class文件不在当前目录而在e盘的TEMP目录下时,可以使用如下的命令行语句:

java -classpath e:\temp Test

来运行这个程序

##包的使用

Java提供了丰富的标准类来帮助程序设计者更方便快捷地编写程序,这些标准类组成了类包,主要有:

  • java.lang
  • java.applet
  • java.io
  • java.net
  • java.util
  • java.awt
  • java.awt.image
  • java.awt.peer

使用Swing组件进行GUI设计,使用javax.swing包中的类

==除了java.lang之外,其余类包都不是java语言所必须的。==
若要使用,必须进行包的导入。

将类组织成包的目的是为了更好地利用包中的类。 通常一个类只能引用与它在同一个包中的类。

如果需要使用其它包中的public类,则可以使用如下的几种方法。

  • (1) 在引入的类前加包名
    例如:
pack1.pack2.Format.format(23.4533,2); 
  • (2) 单类型导入(single-type-import)
    例如上面的语句在程序开始处增加了:
import  pack1.pack2.Format

语句之后,就可以直接写成:

Format.format(23.4533,2);
  • (3) 按需类型导入(type-import-on-demand)
import pack1.pack2.*; 

类型转换和instanceof运算符

基本数据类型可以进行强制或自动类型转换,也可以把一个类的对象转换为继承链中的另一个对象。 将子类的实例转换为父类的实例总是可行的,因为子类的实例也是父类的实例。例如

Shapes[0] = point;    

等价于

Shapes[0] = (Shape)point;

把父类对象转换为子类对象时,必须使用强制类型转换,为使转换成功,必须确保转换的对象是子类的一个实例。

进行转换时确保该对象是另一个类的实例,可以利用instanceof运算符来完成。如:

Point point1 = new Point();  
// Point是Circle的父类
if(point1 instanceof Circle) {
	Circle circle1 = (Circle)point1;
}

父类对象与和子类对象的转化需要注意如下的原则:

  • (1) 子类对象可以被视为是其父类的一个对象;
  • (2) 父类对象不能当成是其某一个子类的对象;
  • (3)如果一个方法的形式参数是父类对象,那么调用这个方法时,可以使用子类对象作为实际参数;
  • (4) 如果父类对象引用指向的实际是一个子类对象,那么这个父类对象的引用可以用强制类型转换转化成子类对象的引用,但在转换之前要使用instanceof运算符进行判断。

Cloneable接口

接口包含常量和抽象方法,但是Cloneable接口例外,Cloneable接口定义如下:

public interface Cloneable{
}

Cloneable接口的定义体为空,定义体为空的接口称为标记接口。Java系统要求,可复制的类需要实现Cloneable接口,利用定义在Object类中的clone()方法,可以复制可复制类的对象。


本文档 Github : https://github.com/bushehui/Java_tutorial

<script type="text/javascript" async src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"> </script> <script type="text/x-mathjax-config"> MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}}); </script>