内部类
1.内部类对外围类的访问
非静态的内部类默认地持有一个外围类的对象的引用,这使得内部类可以随意的访问外围类的所有成员的访问权(包括private).这是源于当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用,然后,在你访问这个外围类的成员时,就是用那个引用来选择外围类的成员.但这一切都是编译器帮我们默默实现的,我们只需要直接使用就可以了.
例如下面这个例子中,在SequenceSelector这个内部类中使用了外围类中的private属性的items数组.
package day13;
public class Sequence {
//选择器
interface Selector{
boolean end(); //判断是否到底
Object current(); //返回当前对象
void next(); //移动到下一个对象
}
private Object[] items;//存放对象的数组
private int next = 0; //确定下一个加入对象的存放位置
public Sequence(int size) {
items = new Object[size];
}
public void add(Object x) {
if (next < items.length) {
items[next++] = x;
}
}
//选择器的实现
private class SequenceSelector implements Selector {
private int i = 0;
@Override
public boolean end() {
return i == items.length; //使用了外部类的private属性的items
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if(i < items.length) i++;
}
}
//用于返回选择器到类外
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args){
Sequence s = new Sequence(10);
for(int i = 0;i <10;i++){
s.add(Integer.toString(i));
}
Selector sl = s.selector();
while(!sl.end()) {
System.out.println(sl.current());
sl.next();
}
}
}
2…this引用外围类
通过使用外围类名加.this可以获取外围类的引用,并由此访问外围类的任意成员(包括private成员)
例:
package day13;
public class Outer {
//外围类的private成员
private int i = 10;
//外围类Outer的fun();
void fun(){System.out.println("Outer's fun()");}
//内部类
public class Inner{
Outer getOuter(){return Outer.this;} //返回外围类的引用
void fun_0(){Outer.this.fun();} //访问外围类中的fun()成员方法.
}
public Inner getInner(){return new Inner();}
public static void main(String[] args){
Outer o1 = new Outer(); //先获取外围类
Inner n1 = o1.getInner(); //获取内部类
n1.getOuter().fun(); //调用内部类getOuter()获取外围类的引用,再调用外围类的fun();
System.out.println(n1.getOuter().i); //访问外围类的private成员
}
}
3…new创建外围类中的内部类对象
为了创建某个内部类的对象,直接new内部类名是不行的:
如下面这样:
package day13;
class C{
public class Inner{}
}
public class DotNew{
public static void main(String[] args) {
C.Inner ci = new Inner(); //直接new
C.Inner ci = new C.Inner(); //引用new,也不行
}
}
/*运行结果:
java: 找不到符号
符号: 类 Inner
位置: 类 day13.DotNew
*/
那怎么样才能创建一个内部类呢,除了之前通过在类中定义一个返回内部类的方法以外,还可以使用外围类名.new来创建内部类,如下:
package day13;
class C{
public class Inner{} //内部类
}
public class TEST{
public static void main(String[] args) {
C c = new C();
C.Inner ci = c.new Inner();
}
}
这里必须注意的是,在拥有外围类对象之前是不可能通过这种方式创建内部类,如上面例子中引用的是一个已经实例化的外围类对象c,通过c.new来引用创建的内部类.当然如果你使用的是static静态内部类(嵌套类),那么直接引用外部类名就可以了,如下这样:
package day13;
class C{
public static class Inner{} //静态内部类
}
public class DotNew{
public static void main(String[] args) {
C c = new C();
C.Inner ci = new C.Inner(); //直接引用C.Inner();
}
}
4.内部类与向上转型
当定义了一个接口,而我们利用内部类去实现它,然后通过向上转型得到此接口的引用,这样做可以很方便的隐藏实现的全部细节,因为内部类的实现我们能够完全隐藏起来,并且不可用(private,protected)
如下这个例子:
定义了两个接口Apple和Pear,在类中通过private和protected实现接口,然后提供接口的引用,这样在获取接口的时候,其实现过程是完全不可见的.
package day13;
//Apple接口
interface pple{
String what();
}
//Pear接口
interface Pear{
int Price();
}
class C7{
//通过内部类 private实现Apple接口
private class PrivateApple implements Apple {
@Override
public String what() {
return "Apple";
}
}
//通过内部类 protected实现Pear接口
protected class ProtectedPear implements Pear{
@Override
public int Price() {
return 99;
}
}
//将私有的子类PrivateApple向上转型后的返回公有Apple对象引用
public Apple Apple(){
return new PrivateApple();
}
//将保护的子类ProtectedPear向上转型后返回公有的Pear对象引用
public Pear Pear(){
return new ProtectedPear();
}
}
public class TEST7 {
public static void main(String[] args){
C7 c = new C7();
Apple apple = c.Apple(); //完全不可见实现过程,看不见内部的PrivateApple类
Pear pear = c.Pear();
}
}
5.匿名内部类
匿名内部类的代码段中不允许对传入变量进行修改,因此传入的参数必须是final类型,如下面代码中的Base getBase(final int x),如果在代码段中进行 x+= 2是错误的,正如所说,不允许修改。其次调用传参构造也只需按照一般new 类名(参数)即可.因为匿名内部类是没有名字的,因此如果需要做一些类似于构造函数的事情,只能通过重写基类的一个方法,如下所示的fun(),然后实例化后再调用它,达到类似于构造的效果。
package day13;
abstract class Base {
private int x;
//构造函数
Base(int x) {
this.x = x;
}
//一个显示X的值的方法
void showX() {
System.out.println("X = " + x);
}
//等待重写用于模仿构造方法
abstract void fun();
}
class Son {
Base getBase(final int x) {
return new Base(x) {//匿名内部类开始
void showX() {
System.out.println("X = " + x * 2);
}
//重写一个基类中的方法,模仿构造函数的意义
public void fun() {
System.out.println("fun()模仿构造被调用了");
//在这里可以写一些类似构造的操作,但这里省略不写,以免代码过于冗杂.
}
};
}
}
public class TEST12 {
public static void main(String[] args){
Son s = new Son();
Base b = s.getBase(20);
b.fun(); //模仿构造
b.showX();
}
}
/*运行结果:
fun()模仿构造被调用了
X = 40
*/
6.简单工厂模式与匿名内部类
这里可能内容有点多,但是很简单,耐心一步一步看就能懂.
先看一个不用匿名内部类的简单工厂模式:
这里主要是一个水果类工厂,其中有一个苹果工厂和一个梨子工厂,通过选择具体的工厂来生成对应的水果.
package day11;
interface Fruit{
String getName();
int getPrice();
}
//在下一个例子中将被匿名内部类替换掉的内容
class Apple implements Fruit{
@Override
public String getName() {
return "Apple";
}
@Override
public int getPrice() {
return 10;
}
}
//这里也是即将被匿名内部类替换的内容
class Pear implements Fruit{
@Override
public String getName() {
return "Pear";
}
@Override
public int getPrice() {
return 15;
}
}
interface Factory{
Fruit getFruit();
}
class AppleFactory implements Factory{
@Override
public Fruit getFruit() {
return new Apple();
}
}
class PearFactory implements Factory{
@Override
public Fruit getFruit() {
return new Pear();
}
}
public class Test0 {
public static void main(String[] args){
Factory appleFactory = new AppleFactory();
Fruit apple = appleFactory.getFruit();
System.out.println("fruit is " + apple.getName());
}
}
接下来使用匿名内部进行替换,其实本质就是将重写Fruit的内容移到了new的地方而已,一张图就看明白了,图中以苹果工厂为例:
使用了匿名内部类后的代码:
package day11;
interface Fruit{
String getName();
int getPrice();
}
interface Factory{
Fruit getFruit();
}
class AppleFactory implements Factory{
@Override
public Fruit getFruit() {
return new Fruit(){ //匿名内部类重写代码块
@Override
public String getName() {
return "Apple";
}
@Override
public int getPrice() {
return 10;
}
};
}
}
class PearFactory implements Factory{
@Override
public Fruit getFruit() {
return new Fruit(){//匿名内部类重写代码块
@Override
public String getName() {
return "Pear";
}
@Override
public int getPrice() {
return 15;
}
};
}
}
public class Test0 {
public static void main(String[] args){
Factory appleFactory = new AppleFactory();
Fruit apple = appleFactory.getFruit();
System.out.println("fruit is " + apple.getName());
}
}
/*运行结果:
fruit is Apple
*/
上面的替换仅仅是具体水果类的替换,下面还有这样更高级的替换:
还是先看一下图解:
package day11;
//抽象水果类
interface Fruit{
String getName();
int getPrice();
}
//工厂
interface Factory{
Fruit getFruit();
}
//苹果类(自带匿名工厂)
class Apple implements Fruit{
//将构造函数设为私有,可以让类外不能实例化这个类,达到内部实现过程隐藏
private Apple(){}
//重写Apple
@Override
public String getName() {
return "Apple";
}
@Override
public int getPrice() {
return 10;
}
public static Factory factory = new Factory() { //匿名工厂类
@Override
public Fruit getFruit() {
return new Apple();
}
};
}
//梨子类(自带匿名工厂)
class Pear implements Fruit{
//同样的道理私有化构造方法
private Pear(){}
//重写Pear
@Override
public String getName() {
return "Pear";
}
@Override
public int getPrice() {
return 15;
}
public static Factory factory = new Factory() { //匿名工厂类
@Override
public Fruit getFruit() {
return new Pear();
}
};
}
public class Test0 {
public static void main(String[] args){
Fruit apple = Apple.factory.getFruit(); //创造苹果
System.out.println(apple.getName());
Fruit pear = Pear.factory.getFruit(); //创造梨子
System.out.println(pear.getName());
}
}
/*运行结果:
Apple
Pear
*/
一个模拟数据库的工厂模式实例:
package day14;
import com.sun.org.apache.xpath.internal.operations.Or;
//抽象数据库
interface Databese{
String getName();
String getIp();
}
//抽象工厂
interface Factory{
Databese getDatabase(String ip);
}
//具体数据库
class MySQL implements Databese{
String ip;
private MySQL(String ip){this.ip = ip;}; //禁止MySQL这个类直接实例化
@Override
public String getName() {
return "MySQL";
}
@Override
public String getIp() {
return ip;
}
public static Factory Factory(){
return new Factory() {
@Override
public Databese getDatabase(String ip) {
return new MySQL(ip);
}
};
}
}
class Oracle implements Databese {
String ip;
//禁止直接实例化
private Oracle(String ip) {
this.ip = ip;
}
@Override
public String getName() {
return "Oracle";
}
@Override
public String getIp() {
return ip;
}
public static Factory Factory(){
return new Factory(){
@Override
public Databese getDatabase(String ip) {
return new Oracle(ip);
}
};
}
}
//实现
public class Demo4 {
public static void main(String[] args){
Databese mySql = MySQL.Factory().getDatabase("192.168.1.1");
System.out.println("MySQL's name : " + mySql.getName() + "\tMySQL's ip : " + mySql.getIp());
Databese oracle = Oracle.Factory().getDatabase("127.0.0.1");
System.out.println("Oracle's name : " + oracle.getName() + "\tOracle's ip : " + oracle.getIp());
}
}
7.最简单的控制框架
通过将变的与不变的进行分离,让变的部分可以被重写,这是最基本的模板框架.在事件控制框架中,将定义的事件需要触发的action()进行分离,根据不同的需要重写不同的行为,以此达到框架的目的:
package day15;
import java.util.*;
abstract class Event{
private long eventTime; //触发action的时间
protected final long delayTime; //从当前时间开始,隔delayTime就这么久后触发action
//构造设置delayTime
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
//确定开始时间
public void start(){
eventTime = System.nanoTime() + delayTime;
}
//确定事件是否可以运行了
public boolean ready(){
return System.nanoTime() >= eventTime;
}
//运行采取的行为,这是每个事件可能不同的地方,让它可以被重写且必须被重写
public abstract void action();
}
class Controller {
//事件列表
private List<Event> eventList = new ArrayList<Event>();
//添加新事件到列表中
public void addEvent(Event e) {
eventList.add(e);
}
//在事件列表中检索有无可以开始运行的事件,有就运行并将它从事件列表中移除
public void run() {
while (eventList.size() > 0) {
for (Event e : eventList) { //为什么要重新new一个ArrayList???
if (e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
}
}
}
public class demo6 {
public static void main(String[] args) {
}
}