《疯狂java讲义》学习(18):内部类

版权声明:本文为博主原创文章,如若转载请注明出处 https://blog.csdn.net/tonydz0523/article/details/86557791

内部类

大部分时候,我们把类定义成一个独立的程序单元。在某些情况下,我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有的地方也叫嵌套类),包含内部类的类也被称为外部类(有的地方也叫宿主类),内部类主要作用:

  • 提供更好地封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。假设需要创建Cow类,Cow类需要组合一个CowLeg对象,CowLeg类只有在Cow类里才有效,离开了Cow类之后没有任何意义。在这种情况下,就可把CowLeg定义成Cow的内部类,不允许其他类访问CowLeg。
  • 内部类成员可以直接访问外部类的私有数据,因为内部类被当成外部类成员,同一类的成员之间可以互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
  • 匿名内部类适合用于创建那些仅需要一次使用的类。对于前面介绍的命令模式,当需要传入一个Command对象时,重新专门定义PrintCommand和AddCommand两个实现类可能没有太大的意义,因为这两个实现类可能仅需要使用一次。在这种情况下,使用匿名内部类将更方便。

非静态内部类

大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类是一种与Field、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类是静态内部类,没有使用static修饰的成员内部类是非静态内部类。
因为内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰。
下面程序在Cow类里定义了一个CowLeg非静态内部类,并在CowLeg类的实例方法中直接访问Cow的private访问权限的实例Field:

public class Cow
{
    private double weight;
    //外部类的两个重载的构造器
    public Cow(){}
    public Cow(double weight)
    {
        this.weight=weight;
    }
    //定义一个非静态内部类
     private class CowLeg{
        //非静态内部类的两个Field
         private double length;
         private String color;
        // 非静态内部类的两个重载的构造器
         public CowLeg(){}
         public CowLeg(double length , String color){
             this.length=length;
             this.color=color;
         }
         public void setLength(double length){
            this.length=length;
         }
         public double getLength(){
            return this.length;
         }
         public void setColor(String color){
            this.color=color;
         }
         public String getColor(){
            return this.color;
        }
        // 非静态内部类的实例方法
         public void info(){
            System.out.println("当前牛腿颜色是:"+ color + ", 高:" + length);
         // 直接访问外部类的private修饰的Field
            System.out.println("本牛腿所在奶牛重:" + weight);// ①
         }
     }
     public void test()
    {
        CowLeg cl=new CowLeg(1.12 , "黑白相间");
        cl.setLength(1.22);
        cl.info();
    }
    public static void main(String[] args)
    {
        Cow cow=new Cow(378.9);
        cow.test();
    }
}

外部类Cow里包含了一个test方法,该方法里创建了一个CowLeg对象,并调用该对象的info方法。读者不难发现,在外部类里使用非静态内部类时,与平时使用普通类并没有太大的区别。
编译上面程序,看到在文件所在路径生成了两个class文件,一个是Cow.class,另一个是Cow C o w L e g . c l a s s C o w c l a s s C o w L e g c l a s s c l a s s O u t e r C l a s s CowLeg.class,前者是外部类Cow的class文件,后者是内部类CowLeg的class文件,即成员内部类(包括静态内部类、非静态内部类)的class文件总是这种形式:OuterClass InnerClass.class。
前面提到过,在非静态内部类里可以直接访问外部类的private成员,上面程序中①号粗体代码行,就是在CowLeg类的方法内直接访问其外部类的private实例变量。这是因为在非静态内部类对象里,保存了一个它寄存的外部类对象的引用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,而非静态内部类实例必须寄存在外部类实例里)。
如果外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可通过使用this、外部类类名.this作为限定来区分。如下程序所示:

public class DiscernVariable
{
    private String prop="外部类的实例变量";
    private class InClass
    {
        private String prop="内部类的实例变量";
        public void info()
        {
            String prop="局部变量";
            //通过 外部类类名.this.varName 访问外部类实例Field
            System.out.println("外部类的Field值:"+ DiscernVariable.this.prop);
            // 通过 this.varName 访问内部类实例的Field
            System.out.println("内部类的Field值:" + this.prop);
            // 直接访问局部变量
            System.out.println("局部变量的值:" + prop);
        }
    }
    public void test()
    {
        InClass in=new InClass();
        in.info();
    }
    public static void main(String[] args)
    {
        new DiscernVariable().test();
    }
}

运行程序,结果如下:

外部类的Field值:外部类的实例变量
内部类的Field值:内部类的实例变量
局部变量的值:局部变量

非静态内部类的成员可以访问外部类的private成员,但反过来就不成立了。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显式创建非静态内部类对象来调用访问其实例成员。
根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。总之,不允许在外部类的静态成员中直接使用非静态内部类

静态内部类

如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,或静态内部类。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。下面程序就演示了这条规则:

public class StaticInnerClassTest
{
    private int prop1=5;
    private static int prop2=9;
    static class StaticInnerClass
    {
        //静态内部类里可以包含静态成员
         private static int age;
         public void accessOuterProp()
        {
            //下面代码出现错误
            //静态内部类无法访问外部类的实例成员
//             System.out.println(prop1);                
            // 下面代码正常
            System.out.println(prop2);
        }
    }
}

静态内部类是外部类的一个静态成员,因此外部类的静态方法、静态初始化块中可以使用静态内部类来定义变量、创建对象等。
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。下面程序示范了这条规则:

public class AccessStaticInnerClass
{
    static class StaticInnerClass
    {
        private static int prop1=5;
        private int prop2=9;
    }
    public void accessInnerProp()
    {
//        System.out.println(prop1);
        //上面代码出现错误,应改为如下形式
        //通过类名访问静态内部类的类成员
         System.out.println(StaticInnerClass.prop1);
//         System.out.println(prop2);
        //上面代码出现错误,应改为如下形式
        //通过实例访问静态内部类的实例成员
        System.out.println(new StaticInnerClass().prop2);    
    }
}

除此之外,Java还允许在接口里定义内部类,接口里定义的内部类默认使用public static修饰,也就是说,接口内部类只能是静态内部类。
如果为接口内部类指定访问控制符,则只能指定public访问控制符;如果定义接口内部类时省略访问控制符,则该内部类默认是public访问控制权限。

使用内部类

定义类的主要作用就是定义变量、创建实例和作为父类被继承。定义内部类的主要作用也如此,但使用内部类定义变量和创建实例则与外部类存在一些小小的差异。下面分三种情况讨论内部类的用法:

1.在外部类内部使用内部类

从前面程序中可以看出,在外部类内部使用内部类时,与平常使用普通类没有太大的区别。一样可以直接通过内部类类名来定义变量,通过new调用内部类构造器来创建实例。
唯一存在的一个区别是:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。
在外部类内部定义内部类的子类与平常定义子类也没有太大的区别。

2.在外部类以外使用非静态内部类

如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用private访问控制权限,private修饰的内部类只能在外部类内部使用。对于使用其他访问控制符修饰的内部类,则能在访问控制符对应的访问权限内使用。

  • 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
  • 使用protected修饰的内部类,可被与外部类处于同一个包中的其他类和外部类的子类所访问。
  • 使用public修饰的内部类,可以在任何地方被访问。

在外部类以外的地方定义内部类(包括静态和非静态两种)变量的语法格式如下:

OuterClass.InnerClass varName

从上面语法格式可以看出,在外部类以外的地方使用内部类时,内部类完整的类名应该是OuterClass.InnerClass。如果外部类有包名,则还应该增加包名前缀。
因为非静态内部类的对象必须寄存在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类以外的地方创建非静态内部类实例的语法如下:

OuterInstance.new InnerConstructor()

从上面语法格式可以看出,在外部类以外的地方创建非静态内部类实例必须使用外部类实例和new来调用非静态内部类的构造器。下面程序示范了如何在外部类以外的地方创建非静态内部类的对象,并把它赋给非静态内部类类型的变量

class Out
{
    //定义一个内部类,不使用访问控制符
    //即只有同一个包中的其他类可访问该内部类
    class In
    {
        public In(String msg)
        {
            System.out.println(msg);
        }
    }
}
public class CreateInnerInstance
{
    public static void main(String[] args)
    {
        Out.In in=new Out().new In("测试信息");
    /*
    上面代码可改为如下三行代码:
    使用OutterClass.InnerClass的形式定义内部类变量
    Out.In in;
    创建外部类实例,非静态内部类实例将寄存在该实例中
    Out out=new Out();
    通过外部类实例和new来调用内部类构造器创建非静态内部类实例
    in=out.new In("测试信息");
    */
    }
}

如果需要在外部类以外的地方创建非静态内部类的子类,则尤其要注意上面的规则:非静态内部类的构造器必须通过其外部类对象来调用。
我们知道:当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。下面程序定义了一个子类继承了Out类的非静态内部类In类:

public class SubClass extends Out.In
{
    //显示定义SubClass的构造器
    public SubClass(Out out)
    {
        //通过传入的Out对象显式调用In的构造器
         out.super("hello");
     }
}

从上面代码中可以看出,如果需要创建SubClass对象时,必须先创建一个Out对象。这是合理的,因为SubClass是非静态内部类In类的子类,非静态内部类In对象里必须有一个对Out对象的引用,其子类SubClass对象里也应该存在一个Out对象的引用。当创建SubClass对象时传给该构造器的Out对象,就是SubClass对象里Out对象引用所指向的对象。
非静态内部类In对象和SubClass对象都必须保留有指向Outer对象的引用,区别是创建两种对象时传入Out对象的方式不同:当创建非静态内部类In类的对象时,必须通过Outer对象来调用new关键字;当创建SubClass类的对象时,必须使用Outer对象作为调用者来调用In类的构造器。

3.在外部类以外使用静态内部类

因为静态内部类是外部类类型关的,因此创建内部类对象时无需创建外部类对象。在外部类以外的地方创建静态内部类的例子:

class StaticOut
{
    //定义一个静态内部类,不使用访问控制符
    //即同一个包中的其他类可访问该内部类
    static class StaticIn
    {
        public StaticIn()
        {
            System.out.println("静态内部类的构造器");
        }
    }
}
public class CreateStaticInnerInstance
{
    public static void main(String[] args)
    {
        StaticOut.StaticIn in=new StaticOut.StaticIn();
    /*
      上面代码可改为如下两行代码:
      使用OuterClass.InnerClass的形式定义内部类变量
      StaticOut.StaticIn in;
      通过new来调用内部类构造器创建静态内部类实例
      in=new StaticOut.StaticIn();
    */
    }
}

从上面代码中可以看出,不管是静态内部类还是非静态内部类,它们声明变量的语法完全一样。区别只是在创建内部类对象时,静态内部类只需使用外部类即可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。

局部内部类

如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。由于局部内部类不能在外部类的方法以外的地方使用,因此局部内部类也不能使用访问控制符和static修饰符修饰。
对于局部成员而言,不管是局部变量还是局部内部类,它们的上一级程序单元都是方法,而不是类,使用static修饰它们没有任何意义。因此,所有的局部成员都不能使用static修饰。不仅如此,因为局部成员的作用域是所在方法,其他程序单元永远也不可能访问另一个方法中的局部成员,所以所有的局部成员都不能使用访问控制符修饰。
如果需要用局部内部类定义变量、创建实例或派生子类,那么都只能在局部内部类所在的方法内进行。

public class LocalInnerClass
{
    public static void main(String[] args)
    {
        //定义局部内部类
        class InnerBase
        {
            int a;
        }
        //定义局部内部类的子类
        class InnerSub extends InnerBase{
            int b;
        }
        //创建局部内部类的对象
        InnerSub is=new InnerSub();
        is.a=5;
        is.b=8;
        System.out.println("InnerSub对象的a和b Field是:"
                + is.a + "," + is.b);
    }
}

编译上面程序,看到生成了三个class文件:LocalInnerClass.class、LocalInnerClass$1InnerBase.class和LocalInnerClass 1 I n n e r S u b . c l a s s c l a s s O u t e r C l a s s 1InnerSub.class,这表明局部内部类的class文件总是遵循如下命名格式:OuterClass NInnerClass.class。注意到局部内部类的class文件的文件名比成员内部类的class文件的文件名多了一个数字,这是因为同一个类里不可能有两个同名的成员内部类,而同一个类里则可能有两个以上同名的局部内部类(处于不同方法中),所以Java为局部内部类的class文件名中增加了一个数字,用于区分。

匿名内部类

匿名内部类适合创建那种只需要一次使用的类,匿名内部类的语法有点奇怪,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用,定义格式如下:

new 父类构造器(实参列表)|实现接口()
{
    //匿名内部类的类体部分
}

从上面定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。
关于匿名内部类还有如下两条规则。

  • 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类。
  • 匿名内部类不能定义构造器,因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块,通过实例初始化块来完成构造器需要完成的事情。

最常用的创建匿名内部类的方式是需要创建某个接口类型的对象,如下程序所示:

interface Product
{
    public double getPrice();
    public String getName();
}
public class AnonymousTest
{
    public void test(Product p)
    {
        System.out.println("购买了一个" + p.getName()
                + ",花掉了" + p.getPrice());
    }
    public static void main(String[] args)
    {
        AnonymousTest ta=new AnonymousTest();
        //调用test方法时,需要传入一个Product参数
        //此处传入其匿名实现类的实例
        ta.test(
            new Product()
            {
                public double getPrice(){
                    return 567.8;
                }
                public String getName(){
                    return "AGP显卡";
                }
            }
        );
    }
}

上面程序中的AnonymousTest类定义了一个test方法,该方法需要一个Product对象作为参数,但Product只是一个接口,无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入该方法——如果这个Product接口实现类需要重复使用,则应该将该实现类定义成一个独立类;如果这个Product接口实现类只需一次使用,则可采用上面程序中的方式,定义一个匿名内部类。
正如上面程序中看到的,定义匿名内部类无需class关键字,而是在定义内名内部类时直接生成该匿名内部类的对象。上面粗体字代码部分就是匿名内部类的类体部分。
由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。对于上面创建Product实现类对象的代码,可以拆分成如下代码:

interface Product
{
    public double getPrice();
    public String getName();
}
class AnonymousProduct implements Product{
    public double getPrice(){return 567.8;}
    public String getName(){return "AGP显卡";}
    public void test(Product p)
    {
        System.out.println("购买了一个" + p.getName()
                + ",花掉了" + p.getPrice());
    }

    public static void main(String[] args)
    {
        AnonymousProduct ta=new AnonymousProduct();
        ta.test(new AnonymousProduct());
    }
}

对比两端代码,他们完全一样,采用匿名内部类的写法更加简洁。
当通过实现接口来创建匿名内部类,匿名内部类也不能显示创建构造器,因此匿名内部类只有一个隐式的无参数构造器,故new接口名后的括号里不能传入参数值。
但如果通过集成父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参列表:

abstract class Device
{
    private String name;
    public abstract double getPrice();
    public Device(){}
    public Device(String name)
    {
        this.name=name;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name=name;
    }
}
public class AnonymousInner
{
    public void test(Device d)
    {
        System.out.println("购买了一个" + d.getName()
                + ",花掉了" + d.getPrice());
    }
    public static void main(String[] args)
    {
        AnonymousInner ai=new AnonymousInner();
        //调用有参数的构造器创建Device匿名实现类的对象
        ai.test(new Device("电子示波器"){public double getPrice(){return 67.8;}});
        //调用无参数的构造器创建Device匿名实现类的对象
        Device d=new Device(){ //初始化块
            {
                System.out.println("匿名内部类的初始化块...");
            }
            // 实现抽象方法
            public double getPrice(){return 56.2;}
            // 重写父类的实例方法
            public String getName(){return "键盘";}
        };
        ai.test(d);
    }
}

上面程序创建了一个抽象父类Device类,这个抽象父类里包含两个构造器:一个无参数的,一个有参数的。当创建以Device为父类的匿名内部类,既可以传入参数,也可以不传入参数。
当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法。如果匿名内部类需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量,否者系统将报错。

闭包(Closure)和回调

闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域信息。对于非静态内部类而言,它不仅记录了其外部类的详细信息,还保留了一个创建非静态内部类对象的引用,并且可以直接调用外部类的private成员,因此可以把非静态内部类当成面向对象领域的闭包。
通过这种仿闭包的非静态内部类,可以很方便地实现回调功能,回调就是某个方法一旦获得类内部类对象的引用后,就可以在核实的时候反过来取调用外部类实例的方法。所谓回调,就是允许客户类通过内部类引用来调用其外部类的方法,这是一种非常灵活的功能。
假设有下面的Teachable接口和Programmer基类,他们都提供了一个work方法,这两个方法的方法签名完全相同,但方法功能可能不一样:

interface Teachable
{
    void work();
}
public class Programmer
{
    private String name;
    //Programmer类的两个构造器
    public Programmer(){}
    public Programmer(String name)
    {
        this.name=name;
    }
    //此处省略了name的setter和getter方法
    public void work()
    {
        System.out.println(name + "在灯下认真敲键盘...");
    }
}

假设现在有一个人(如笔者一样),既是一个程序员,也是一个教师。也就是说,需要定义一个特殊的类,既需要实现Teachable接口,也需要继承Programmer父类。表面上看起来这没有任何问题,问题是Teachable接口和Programmer父类里包含了相同的work方法,如果采用如下代码来定义一个特殊的TeachableProgrammer类:

public class TeachableProgrammer extends Programmer implements Teachable
{
    public void work()
    {
        System.out.println(getName() + "教师在讲台上讲解...");
    }
}

显然上面的TeachableProgrammer类只有一个work方法,这个work方法只能进行“教学”,而不能进行“编程”。但是实际上需要TeachableProgrammer类里既包含“教学”的work方法,也包含“编程”的work方法。
这个时候,我们可以通过一个仿闭包的内部类来实现这个功能:

public class TeachableProgrammer extends Programmer
{
    public TeachableProgrammer(){}
    public TeachableProgrammer(String name)
    {
        super(name);
    }
    //教学工作依然由TeachableProgrammer类定义
    private void teach()
    {
        System.out.println(getName() + "教师在讲台上讲解...");
    }
    private class Closure implements Teachable
    {
        /*
        非静态内部类回调外部类实现work方法,非静态内部类引用的作用仅仅是
        向客户类提供一个回调外部类的途径
        */
        public void work(){teach();}}
    //返回一个非静态内部类引用,允许外部类通过该非静态内部类引用来回调外部类的方法
    public Teachable getCallbackReference()
    {
        return new Closure();
    }
}

上面的TeachableProgrammer类只是Programmer类的子类,它可以直接调用Programmer基类的work方法,该类也包含教学的teach方法,但这个方法与Teachable接口没有任何关系,TeachableProgrammer也不能当成Teachable使用。此时创建了一个Closure内部类,它实现了Teachable接口,并实现了教学的work方法——但这种实现是通过回调TeachableProgrammer类的teach方法实现的。如果需要让TeachableProgrammer对象进行教学,只需调用Closure内部类(它是Teachable接口的实现类)对象的work方法即可。
TeachableProgrammer类提供了一个获取内部类对象的方法,该方法无须返回Closure类型,只需返回所实现接口:Teachable类型即可,接下来它就可当成一个Teachable对象使用了。
下面程序示范了如何让TeachableProgrammer对象执行“教学”的work方法,也执行“编程”的work方法:

public class TeachableProgrammerTest
{
    public static void main(String[] args)
    {
        TeachableProgrammer tp=new TeachableProgrammer("ffzs");
        //直接调用TeachableProgrammer类从Programmer类继承到的work方法
        tp.work();
        // 表面上调用的是Closure的work方法
        //实际上是回调TeachableProgrammer的teach方法
        tp.getCallbackReference().work();
    }
}

java编程练习

使用局部内部类实现闹钟应用

日常生活中,我们经常要用到闹钟,使用它可以更好地帮助人们安排时间。本实例将实现一个非常简单的闹钟。运行本实例,控制台会不断输出当前时间,并且每隔一秒会发出提示音。用户可以单击“确定”按钮来退出程序

1.

新建项目LocalInner,并在其中创建一个AlarmClock.java文件。在该类中,首先定义两个属性,一个是delay,表示延迟的时间;另一个是flag,表示是否需要发出提示信息。然后在start()方法中,使用Timer类来安排动作事件:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;

public class AlarmClock {
    private int delay;                                        // 表示延迟时间
    private boolean flag;                                     // 表示是否发出声音
    public AlarmClock(int delay, boolean flag) {              // 使用构造方法初始化各个域
        this.delay = delay;
        this.flag = flag;
    } public void start() {
        class Printer implements ActionListener {           // 定义内部类实现动作监听器
            @Override
            public void actionPerformed(ActionEvent e) {
                SimpleDateFormat format = new SimpleDateFormat("k:m:s");
                String result = format.format(new Date());  // 获得当前时间
                System.out.println("当前的时间是:" + result);// 显示当前时间
                if (flag) {                            // 根据flag来决定是否要发出声音
                    Toolkit.getDefaultToolkit().beep();
                }
            }
        }
        new Timer(delay, new Printer()).start();       // 创建Timer对象并启动
    }
}

2.

然后编写LocalInner.java类对其进行测试,在该类的主方法中创建AlarmClock对象,并调用其start()方法,使用对话框提示用户是否需要退出程序:

package LocalInner;

import javax.swing.JOptionPane;
public class LocalInner {
    public static void main(String[] args) {
        AlarmClock clock = new AlarmClock(1000, true);      // 创建AlarmClock对象
        clock.start();                                      // 启动start()方法
        JOptionPane.showMessageDialog(null, "是否退出?");  // 退出程序
        System.exit(0);
    }
}

使用静态内部类获取数组中的最大和最小值

当对元素进行排序时,需要明确各个元素如何比较大小。使用既定的比较方式,就可以求出一个数组中的最大和最小值。本实例使用静态内部类来实现使用一次遍历获得最大最小值

1.

新建项目StaticInner,并在其中创建一个MaxMin.java文件。在该类的方法中,首先定义一个静态内部类Result,然后在该类中定义两个整型属性——Max和Min,分别代表最大值和最小值。再使用构造方法为其初始化,并提供getXXX()方法来获得这两个数的值。最后定义一个静态方法getResult(),该方法的返回值是Result类型,这样就可以既能保存最大值,又能同时保存最小值了

package StaticInner;

public class MaxMin {
    public static class Result{
        private int max;
        private int min;
        //构造器初始化
        public Result(int max, int min){
            this.max=max;
            this.min=min;
        }
        //获取最大值
        public double getMax(){
            return max;
        }
        //获取最小是
        public double getMin(){
            return min;
        }
    }
    public static Result getResult(int[] array){   //遍历数组获得最大最小值
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        for (int i:array){
            if (i>max){
                max=i;
            }
            if(i<min){
                min=i;
            }
        }
        return new Result(max, min);   //返回Result对象
    }
}

2.

接着我们创建一个测试类StaticInner来对实例进行测试,在main()方法中,使用随机数初始化了一个长度为5的数组,并求得该数组的最大最小值:

package StaticInner;

public class StaticInner {
    public static void main(String[] args){
        int[] array=new int[5];   //初始化数组
        for (int i=0; i < array.length; i++){
            array[i]=(int)(100*Math.random());   //随机获取100内数字
        }
        System.out.println("原数组:");   //显示数组的各个元素
        for (int i=0; i<array.length;i++){
            System.out.print(array[i]+" ");
        }
        System.out.println();
        System.out.println("最大值:"+ MaxMin.getResult(array).getMax());   //显示最大值
        System.out.println("最小值:"+ MaxMin.getResult(array).getMin());   //显示最小值
    }
}

猜你喜欢

转载自blog.csdn.net/tonydz0523/article/details/86557791