java_final、权限、内部类、引用类型

导读:本篇文章讲解 java_final、权限、内部类、引用类型,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

第一章 final关键字

1.1 概述

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承 API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了 final 关键字, 用于修饰不可改变内容。

final: 不可改变。可以用于修饰类、方法和变量。

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,不能被重新赋值。

1.2 使用方式

修饰类
格式如下:

public final class 类名称 {
    // ...
}

查询API发现像 public final class String 、 public final class Math 、 public final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。

含义:当前这个类不能有任何的子类。(太监类)
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没儿子。)

修饰方法
格式如下:

修饰符 final 返回值类型 方法名称(参数列表) {
    // 方法体
}

当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是就算有类继承那么有final关键字的方法不能被覆盖重写。

修饰变量

  1. 局部变量——基本类型

基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:

public class Demo01Final {

    public static void main(String[] args) {
   		//普通的局部变量
        int num1 = 10;
        System.out.println(num1); // 10
        num1 = 20;
        System.out.println(num1); // 20

        // 一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
        // “一次赋值,终生不变”
        final int num2 = 200;
        System.out.println(num2); // 200

//        num2 = 250; // 错误写法!不能改变!
//        num2 = 200; // 错误写法!

        // 正确写法!只要保证有唯一一次赋值即可
        final int num3;
        num3 = 30;
    }

}

思考,如下两种写法,哪种可以通过编译?
写法1:

	final int c = 0; 
	for (int i = 0; i < 10; i++) { 
		c = i; 
		System.out.println(c); 
	}

写法2:

	for (int i = 0; i < 10; i++) { 
		final int c = i; 
		System.out.println(c); 
	}

根据 final 的定义,写法1报错!写法2正确,为什么通过编译呢?因为每次循环,都是一次新的变量c。这也是大家 需要注意的地方。

  1. 局部变量——引用类型

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的 修改,代码如下:

/*
final关键字代表最终、不可改变的。

常见四种用法:
1. 可以用来修饰一个类
2. 可以用来修饰一个方法
3. 还可以用来修饰一个局部变量
4. 还可以用来修饰一个成员变量
 */
public class Demo01Final {

    public static void main(String[] args) {

        // 对于基本类型来说,不可变说的是变量当中的数据不可改变
        // 对于引用类型来说,不可变说的是变量当中的 地址值 不可改变

        final Student stu2 = new Student("高圆圆");
        // 错误写法!final的引用类型变量,其中的地址不可改变
        //stu2 = new Student("赵又廷");//报错,指向了新的对象,地址值改变。
        
        // 调用setName方法
        stu2.setName("高圆圆圆圆圆圆");// 可以修改
        System.out.println(stu2.getName()); // 高圆圆圆圆圆圆
    }

}

  1. 成员变量

对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。

  • 显示初始化;
public class User { 
	final String USERNAME = "张三"; 
	private int age; 
}
  • 构造方法初始化。
/*
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变。

1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
3. 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
 */
public class Person {

    private final String name/* = "鹿晗"*/;//直接赋值

    public Person() {
        name = "关晓彤";  //通过构造方法赋值
    }

    public Person(String name) {
        this.name = name;//通过构造方法赋值
    }
}

被final修饰的常量名称,一般都有书写规范,所有字母都大写

第二章 权限修饰符

2.1 概述

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,

  • public:公共的。

  • protected:受保护的

  • default:默认的

  • private:私有的

2.2 不同权限的访问能力

在这里插入图片描述

可见,public具有最大权限。private则是最小权限。

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用 private ,隐藏细节。

  • 构造方法使用 public ,方便创建对象。

  • 成员方法使用 public ,方便调用方法。

小贴士:不加权限修饰符,其访问能力与default修饰符相同
在这里插入图片描述
在这里插入图片描述

第三章 内部类

1.1 概述

什么是内部类 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类

成员内部类

  • 成员内部类 :定义在类中方法外的类。

定义格式:

修饰符 class 外部类名称 {
    修饰符 class 内部类名称 {
        // ...
    }
    // ...
}

如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。

代码举例:

public class Car { //外部类 
	public class Engine { // 成员内部类
	} 
}

访问特点

  • 内部类可以直接访问外部类的成员,包括私有成员。
  • 外部类要访问内部类的成员,必须要建立内部类的对象。

创建内部类对象格式:

外部类名.内部类名 对象名 = new 外部类型().new 内部类型()

访问演示,代码如下:

public class Body { // 外部类

    // 外部类的成员变量
    private String name;

    public class Heart { // 成员内部类

        //内部类可以直接访问外部类的成员,包括私有成员。
        // 内部类的方法
        public void beat() {
            System.out.println("心脏跳动:蹦蹦蹦!");
            System.out.println("我叫:" + name); // 正确写法!
        }

    }

    //外部类要访问内部类的成员,必须要建立内部类的对象
    // 外部类的方法
    public void methodBody() {
        System.out.println("外部类的方法");
        new Heart().beat();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
     

定义测试类:

/*
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。

分类:
1. 成员内部类
2. 局部内部类(包含匿名内部类)

成员内部类的定义格式:
修饰符 class 外部类名称 {
    修饰符 class 内部类名称 {
        // ...
    }
    // ...
}

注意:内用外,随意访问;外用内,需要内部类对象。

==========================
如何使用成员内部类?有两种方式:
1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
2. 直接方式,公式:
类名称 对象名 = new 类名称();
【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
 */
public class Demo01InnerClass {

    public static void main(String[] args) {
        Body body = new Body(); // 外部类的对象
        // 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
        body.methodBody();
        System.out.println("=====================");

        // 按照公式写:
        Body.Heart heart = new Body().new Heart();
        heart.beat();
    }

}

运行结果:
在这里插入图片描述

内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名 和$符号 。

.
比如,Body$Heart.class

1.2 匿名内部类【重点】

  • 匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。 开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作,
  1. 定义子类
  2. 重写接口中的方法
  3. 创建子类对象
  4. 调用重写后的方法

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快 捷方式。

前提
匿名内部类必须继承一个父类或者实现一个父接口。

格式

new 父类名或者接口名(){ 
	// 方法重写 
	@Override public void method() { 
		// 执行语句 
	} 
};

使用方式
以接口为例,匿名内部类的使用,代码如下:

public interface MyInterface {
    public abstract void fly(); // 抽象方法
}

创建匿名内部类,并调用:

public class DemoMain {

    public static void main(String[] args) {
    	  //不使用匿名内部类
    	  //使用的是实现类调用,我们就需要重写写一个类并且实现接口重写方法
//        MyInterface obj = new MyInterfaceImpl();
//        obj.fly();

//        MyInterface some = new MyInterface(); // 错误写法!

        // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
        MyInterface objA = new MyInterface() {
			//重写接口的方法
            @Override
            public void fly() {
                System.out.println("我要飞......");
            }
        };

        //调用 fly方法,执行重写后的方法
        objA.fly();
   }
}

通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:

    public static void main(String[] args) {
/*
        MyInterface obj = new MyInterfaceImpl();
        obj.fly();
*/

//        MyInterface some = new MyInterface(); // 错误写法!

        // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
        MyInterface objA = new MyInterface() {

            @Override
            public void fly() {
                System.out.println("我要飞......");
            }
        };

        // 将fobjA传递给showFly方法中
        showFly(objA);
    }

    public static  void  showFly (MyInterface m){
        m.fly();
    }

}

也可以简化代码:

public class DemoMain {

    public static void main(String[] args) {
    
       // 使用了匿名内部类,而且省略了对象名称,也是匿名对象
        new MyInterface() {
            @Override
            public void fly() {
                System.out.println("我要飞-----");
            }
        }.fly();

        // 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
        new MyInterface(){

            @Override
            public void fly() {
                System.out.println("飞......");
            }
        }.fly();
    }

}

需要注意的是:
在这里插入图片描述

第四章 引用类型用法总结

实际的开发中,引用类型的使用非常重要,也是非常普遍的。我们可以在理解基本类型的使用方式基础上,进一步 去掌握引用类型的使用方式。基本类型可以作为成员变量、作为方法的参数、作为方法的返回值,那么当然引用类 型也是可以的。

4.1 class作为成员变量

在定义一个类Hero(游戏英雄)时,代码如下:


// 游戏当中的英雄角色类
public class Hero {

    private String name; // 英雄的名字
    private int age; // 英雄的年龄

//省略set/get方法和全参的构造和无参的构造
   
}

使用 int 类型表示 角色年龄,使用 String 类型表示姓名。此时, String 本身就是引用类型,由于使用 的方式类似常量,所以往往忽略了它是引用类型的存在。如果我们继续丰富这个类的定义,给 Hero 增加武器等属性,我们将如何编写呢?

定义武器类:


public class Weapon {

    private String code; // 武器的代号

    public Weapon() {
    }

    public Weapon(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

定义英雄类:

// 游戏当中的英雄角色类
public class Hero {

    private String name; // 英雄的名字
    private int age; // 英雄的年龄

    // 英雄的武器
    private Weapon weapon; 

    public Hero() {
    }

    public Hero(String name, int age, Weapon weapon) {
        this.name = name;
        this.age = age;
        this.weapon = weapon;
    }

    public void attack() {
        System.out.println("年龄为" + age + "的" + name + "用" + weapon.getCode() + "攻击敌方。");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Weapon getWeapon() {
        return weapon;
    }

    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

测试类:

public class DemoMain {

    public static void main(String[] args) {
        // 创建一个英雄角色
        Hero hero = new Hero();
        // 为英雄起一个名字,并且设置年龄
        hero.setName("盖伦");
        hero.setAge(20);

        // 创建一个武器对象
        Weapon weapon = new Weapon("AK-47");
        // 为英雄配备武器
        hero.setWeapon(weapon);

        // 年龄为20的盖伦用AK-47攻击敌方。
        hero.attack();
    }

}

类作为成员变量时,对它进行赋值的操作,实际上,是赋给它该类的一个对象

4.2 interface作为成员变量

接口是对方法的封装,对应游戏当中,可以看作是扩展游戏角色的技能。所以,如果想扩展更强大技能,我们在 Hero 中,可以增加接口作为成员变量,来设置不同的技能。 定义接口:


public interface Skill {

    void use(); // 释放技能的抽象方法

}

定义英雄类:

public class Hero {

    private String name; // 英雄的名称
    private Skill skill; // 英雄的技能

    public Hero() {
    }

    public Hero(String name, Skill skill) {
        this.name = name;
        this.skill = skill;
    }

    public void attack() {
        System.out.println("我叫" + name + ",开始施放技能:");
        skill.use(); // 调用接口中的抽象方法
        System.out.println("施放技能完成。");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }
}

定义测试类:

public class DemoGame {

    public static void main(String[] args) {
        Hero hero = new Hero();
        hero.setName("艾希"); // 设置英雄的名称

        // 设置英雄技能
//        hero.setSkill(new SkillImpl()); // 使用单独定义的实现类

        // 还可以改成使用匿名内部类
//        Skill skill = new Skill() {
//            @Override
//            public void use() {
//                System.out.println("Pia~pia~pia~");
//            }
//        };
//        hero.setSkill(skill);
//		  hero.attack();

        // 进一步简化,同时使用匿名内部类和匿名对象
        hero.setSkill(new Skill() {
            @Override
            public void use() {
                System.out.println("Biu~Pia~Biu~Pia~");
            }
        });

        hero.attack();
    }

}

运行结果:
在这里插入图片描述

我们使用一个接口,作为成员变量,以便随时更换技能,这样的设计更为灵活,增强了程序的扩展性。 接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。

4.3 interface作为方法参数和返回值类型

当接口作为方法的参数时,需要传递什么呢?当接口作为方法的返回值类型时,需要返回什么呢?对,其实都是它的 子类对象。 ArrayList 类我们并不陌生,查看API我们发现,实际上,它是 java.util.List 接口的实现类。所 以,当我们看见 List 接口作为参数或者返回值类型时,当然可以将 ArrayList 的对象进行传递或返回。

请观察如下方法:获取某集合中所有的偶数。

public class Test {
    public static void main(String[] args) {
        // 创建ArrayList集合,并添加数字
        ArrayList<Integer> list =new ArrayList<Integer>();
        for (int i=0;i<10;i++){
            list.add(i);
        }

        /*
        获取偶数集合
        因为getEvenNum方法的参数是List,而ArrayList是List的子类,
        所以srcList可以传递
        */
        List evenNum = getEvenNum(list);
        System.out.println(evenNum);
    }

	//静态方法 返回类型为List 传入的参数为 List泛型为Integer
    public static List<Integer> getEvenNum(List<Integer> list){
        // 创建保存偶数的集合
        ArrayList<Integer> evenList = new ArrayList<Integer>();
        // 遍历集合list,判断元素为偶数,就添加到evenList中

        for (int i = 0; i < list.size(); i++) {
            Integer num=list.get(i);
            if (num% 2==0){//取模
                evenList.add(num);
            }

        }
	    /**
         *  返回偶数
         * 因为getEvenNum方法的返回值类型是List,而ArrayList是List的子类, 所以evenList可以返回
         */
        return evenList;
    }
}

从上面案例中可以发现:

  • 接口作为参数时,传递它的子类对象。
  • 接口作为返回值类型时,返回它的子类对象。

第五章 综合案例——发红包【界面版】

红包文化源远流长。从古时的红色纸包,到手机App中的手气红包,红包作为一种独特的中华文化传承至今。之前 的课程中,我们也编写过程序,模拟发普通红包。那么今天,我们将整合基础班课程中所有的技术和知识,编写一 个带界面版的 发红包 案例。

目前,我们尚未学习过任何与界面相关的类。所以,界面相关代码,已经给出。请运用所学技术分析并使 用。

案例需求
分析并使用已给出的类,编写程序,设置红包类型。

小贴士
.
红包类型:

  1. 普通红包:金额均分。不能整除的,余额添加到最后一份红包中。
  2. 手气红包:金额随机。各个红包金额累和与总金额相等。

红包场景:
此案例是模拟群主给群成员发红包,群主自己打开最后一个红包的场景。

案例分析
已知的类:

  1. RedPacketFrame :一个抽象类,包含了一些属性,是红包案例的页面。
package com.itmei.day11.red;


import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * 红包的框架 RedPacketFrame
 *
 * AWT / Swing / JavaFX
 *
 * @author 不是我
 */
public abstract class RedPacketFrame extends JFrame {

    private static final long serialVersionUID = 1L;
    
    private static final String DIR = "F:\\IDEA\\java学习案例\\java语言入门\\final、权限、内部类\\day11\\pic";

    private ArrayList<Integer> moneyList = null;

    private static int initMoney = 0;
    private static int totalMoney = 0; // 单位为“分”
    private static int count = 0;

    private static HashMap<JPanel, JLabel> panelLable = new HashMap<JPanel, JLabel>();

    // 设置字体
    private static Font fontYaHei = new Font("微软雅黑", Font.BOLD, 20);
    private static Font msgFont = new Font("微软雅黑", Font.BOLD, 20);
    private static Font totalShowFont = new Font("微软雅黑", Font.BOLD, 40);
    private static Font nameFont = new Font("微软雅黑", Font.BOLD, 40);
    private static Font showNameFont = new Font("微软雅黑", Font.BOLD, 20);
    private static Font showMoneyFont = new Font("微软雅黑", Font.BOLD, 50);
    private static Font showResultFont = new Font("微软雅黑", Font.BOLD, 15);

    /**
     * 窗体大小 WIDTH:400 HEIGHT:600
     */
    private static final int FRAME_WIDTH = 416; // 静态全局窗口大小
    private static final int FRAME_HEIGHT = 650;
    private static JLayeredPane layeredPane = null;

    /// private static JPanel contentPane = null;

    /**
     * page1:输入页面 - InputPanel . 组件和初始化!
     */
    private static JPanel inputPanel = new JPanel();

    // private static JTextField input_total = new JTextField("200"); // 测试用
    // private static JTextField input_count = new JTextField("3"); // 测试用
    private static JTextField input_total = new JTextField();
    private static JTextField input_count = new JTextField();
    private static JTextField input_people = new JTextField("30");
    private static JTextField input_msg = new JTextField("恭喜发财  ,  大吉大利");
    private static JTextField input_total_show = new JTextField("$ " + input_total.getText().trim());
    private static JLabel input_inMoney = new JLabel(); // 不可见
    private static JLabel input_bg_label = new JLabel(new ImageIcon(DIR + "\\01_input.jpg"));

    static {

        // 设置位置
        input_total.setBounds(200, 90, 150, 50);
        input_count.setBounds(200, 215, 150, 50);
        input_people.setBounds(90, 275, 25, 30);
        input_msg.setBounds(180, 340, 200, 50);
        input_total_show.setBounds(130, 430, 200, 80);
        input_inMoney.setBounds(10, 535, 380, 65);
        input_bg_label.setBounds(0, 0, 400, 600); // 背景

        // 设置字体
        input_total.setFont(fontYaHei);
        input_count.setFont(fontYaHei);
        input_people.setFont(fontYaHei);
        input_msg.setFont(msgFont);
        input_msg.setForeground(new Color(255, 233, 38)); // 字体颜色 为金色
        input_total_show.setFont(totalShowFont);
        input_inMoney.setFont(fontYaHei);

        // 透明
        input_people.setOpaque(false);
        input_total_show.setOpaque(false);
        // 编 辑 -- 不可编辑
        input_people.setEditable(false);
        input_total_show.setEditable(false);

        // 边界 -- 无
        input_total.setBorder(null);
        input_count.setBorder(null);
        input_people.setBorder(null);
        input_msg.setBorder(null);
        input_total_show.setBorder(null);

    }

    /**
     * page2:打开页面 - openPanel . 组件和初始化!
     */
    private static JPanel openPanel = new JPanel();

    private static JTextField open_ownerName = new JTextField("谁谁谁");
    private static JLabel open_label = new JLabel(new ImageIcon(DIR + "\\02_open_2.gif"));
    private static JLabel open_bg_label = new JLabel(new ImageIcon(DIR + "\\02_open_1.jpg"));

    static {

        // 设置 位置.
        open_ownerName.setBounds(0, 110, 400, 50);
        open_bg_label.setBounds(0, 0, 400, 620);
        open_label.setBounds(102, 280, 200, 200);
        open_ownerName.setHorizontalAlignment(JTextField.CENTER);

        // 设置字体
        open_ownerName.setFont(nameFont);
        open_ownerName.setForeground(new Color(255, 200, 163)); // 字体颜色 为金色

        // 背景色
        // open_name.setOpaque(false);
        open_ownerName.setBackground(new Color(219, 90, 68));

        // 不可编辑
        open_ownerName.setEditable(false);
        // 边框
        open_ownerName.setBorder(null);

    }

    /**
     * page3:展示页面 - showPanel . 组件和初始化!
     */
    private static JPanel showPanel = new JPanel();
    private static JPanel showPanel2 = new JPanel();
    private static JScrollPane show_jsp = new JScrollPane(showPanel2);

    private static JLabel show_bg_label = new JLabel(new ImageIcon(DIR + "\\03_money_1.jpg"));

    private static JTextField show_name = new JTextField("用户名称");
    private static JTextField show_msg = new JTextField("祝福信息");
    private static JTextField show_money = new JTextField("99.99");
    private static JTextField show_result = new JTextField(count + "个红包共" + (totalMoney / 100.0) + "元,被抢光了");

    static {
        // 分别设置水平和垂直滚动条自动出现
        // jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        // jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

        /*
         * 两部分 页面 . 1.本人获得的红包-- showPanel 2.别人获得的红包-- show_jsp
         */
        show_name.setBounds(125, 180, 100, 30);
        show_name.setOpaque(false);
        show_name.setBorder(null);
        show_name.setFont(showNameFont);

        show_msg.setBounds(0, 220, 400, 30);
        show_msg.setOpaque(false);
        show_msg.setBorder(null);
        show_msg.setFont(msgFont);
        show_msg.setHorizontalAlignment(JTextField.CENTER);

        show_money.setBounds(0, 270, 250, 40);
        show_money.setOpaque(false);
        show_money.setBorder(null);
        show_money.setFont(showMoneyFont);
        show_money.setForeground(new Color(255, 233, 38)); // 字体颜色 为金色
        show_money.setHorizontalAlignment(SwingConstants.RIGHT);

        show_result.setBounds(10, 460, 400, 20);
        show_result.setOpaque(false);
        show_result.setBorder(null);
        show_result.setFont(showResultFont);
        show_result.setForeground(new Color(170, 170, 170)); // 字体颜色 为灰色

        // 设置 图片.
        show_bg_label.setBounds(0, 0, 400, 500);

    }

    static {

        // 页面和 背景的对应关系.
        panelLable.put(inputPanel, input_bg_label);
        panelLable.put(openPanel, open_bg_label);
        panelLable.put(showPanel, show_bg_label);
    }

    private void init() {
        // 层次面板-- 用于设置背景
        layeredPane = this.getLayeredPane();
//        System.out.println("层次面板||" + layeredPane);
        // System.out.println(layeredPane);

        // 初始化框架 -- logo 和基本设置
        initFrame();
        // 初始化 三个页面 -- 准备页面
        initPanel();

        // 2.添加 页面 --第一个页面, 输入 panel 设置到 页面上.
        setPanel(inputPanel);

        // 3.添加 监听
        addListener();
    }


    /**
     * 初始化框架 -- logo 和基本设置
     */
    private void initFrame() {
        // logo
        this.setIconImage(Toolkit.getDefaultToolkit().getImage(DIR + "\\logo.gif"));
//        System.out.println("LOGO初始化...");

        // 窗口设置
        this.setSize(FRAME_WIDTH, FRAME_HEIGHT); // 设置界面大小
        this.setLocation(280, 30); // 设置界面出现的位置
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setLayout(null);

        // 测试期 注释 拖 拽 , 运行放开
        // this.setResizable(false);
        this.setVisible(true);
    }

    /**
     * 初始化页面-- 准备三个页面
     */

    private void initPanel() {
//        System.out.println("页面初始化...");
        initInputPanel();
        initOpenPanel();
        initShowPanel();

    }

    private void initInputPanel() {

        inputPanel.setLayout(null);
        inputPanel.setBounds(0, -5, 400, 600);

        // this.add(bg_label);
        inputPanel.add(input_total);
        inputPanel.add(input_count);
        inputPanel.add(input_people);
        inputPanel.add(input_msg);
        inputPanel.add(input_total_show);
        inputPanel.add(input_inMoney);

//        System.out.println("输入页面||" + inputPanel);

    }

    private void initOpenPanel() {
        openPanel.setLayout(null);
        openPanel.setBounds(0, 0, 400, 600);
        // this.add(bg_label);
        openPanel.add(open_ownerName);
        openPanel.add(open_label);
//        System.out.println("打开页面||" + openPanel);
    }

    private void initShowPanel() {
        showPanel.setLayout(null);
        showPanel.setBounds(10, 10, 300, 600);

        // ==============
        showPanel.add(show_name);
        showPanel.add(show_msg);
        showPanel.add(show_money);
        showPanel.add(show_result);
//        System.out.println("展示页面||" + showPanel);
        // ====================================
        // showPanel2.setLayout(null);
        // showPanel2.setBounds(0, 500, 401, 300);

        showPanel2.setPreferredSize(new Dimension(300, 1000));
        showPanel2.setBackground(Color.white);

        show_jsp.setBounds(0, 500, 400, 110);

    }

    /**
     * 每次打开页面, 设置 panel的方法
     */
    private void setPanel(JPanel panel) {
        // 移除当前页面
        layeredPane.removeAll();

//        System.out.println("重新设置:新页面");
        // 背景lable添加到layeredPane的默认层
        layeredPane.add(panelLable.get(panel), JLayeredPane.DEFAULT_LAYER);

        // 面板panel设置为透明
        panel.setOpaque(false);

        // 面板panel 添加到 layeredPane的modal层
        layeredPane.add(panel, JLayeredPane.MODAL_LAYER);
    }

    // private void setShowPanel(JPanel show) {
    // setPanel(show);
    // layeredPane.add(show_jsp, JLayeredPane.MODAL_LAYER);
    //
    // }

    /**
     * 设置组件的监听器
     */
    private void addListener() {

        input_total.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                // System.out.println(e);
                String input_total_money = input_total.getText();
                input_total_show.setText("$ " + input_total_money);
            }
        });

        input_count.addKeyListener(new KeyAdapter() {

            @Override
            public void keyReleased(KeyEvent e) {
                // System.out.println(e);
//                System.out.println("个数:" + input_count.getText());
            }
        });
        input_msg.addKeyListener(new KeyAdapter() {

            @Override
            public void keyReleased(KeyEvent e) {
                // System.out.println(e);
//                System.out.println("留言:" + input_msg.getText());
            }
        });

        input_inMoney.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                try {

                    // 获取页面的值.
                    totalMoney = (int) (Double.parseDouble(input_total.getText()) * 100); // 转换成"分"
                    count = Integer.parseInt(input_count.getText());
                    if (count > 30) {
                        JOptionPane.showMessageDialog(null, "红包个数不得超过30个", "红包个数有误", JOptionPane.INFORMATION_MESSAGE);
                        return;
                    }

                    initMoney = totalMoney;

                    System.out.println("总金额:[" + totalMoney + "]分");
                    System.out.println("红包个数:[" + count + "]个");

                    input_inMoney.removeMouseListener(this);

//                    System.out.println("跳转-->打开新页面");

                    // 设置群主名称
                    open_ownerName.setText(ownerName);
                    // 设置打开页面
                    setPanel(openPanel);

                } catch (Exception e2) {
                    JOptionPane.showMessageDialog(null, "请输入正确【总金额】或【红包个数】", "输入信息有误", JOptionPane.ERROR_MESSAGE);

                }
            }
        });

        // open_ownerName ,点击 [名称],触发的方法 , 提示如何设置群主名称.

        open_ownerName.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent arg0) {
                JOptionPane.showMessageDialog(null, "请通过【setOwnerName】方法设置群主名称", "群主名称未设置",
                        JOptionPane.QUESTION_MESSAGE);
            }
        });

        // open label , 点击 [开],触发的方法,提示如何设置打开方式.
        open_label.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (openWay == null) {
                    JOptionPane.showMessageDialog(null, "请通过【setOpenWay】方法设置打开方式", "打开方式未设置",
                            JOptionPane.QUESTION_MESSAGE);
                    return;
                }

//                System.out.println("跳转-->展示页面");

                moneyList = openWay.divide(totalMoney, count);

//                System.out.println(moneyList);
                /*
                 * showPanel 添加数据
                 *
                 */
                show_name.setText(ownerName);
                show_msg.setText(input_msg.getText());
                if (moneyList.size() > 0) {
                    show_money.setText(moneyList.get(moneyList.size() - 1) / 100.0 + "");
                }
                show_result.setText(count + "个红包共" + (initMoney / 100.0) + "元,被抢光了");

                open_label.removeMouseListener(this);

                setPanel(showPanel);

                // 添加数据
                for (int i = 0; i < moneyList.size(); i++) {

                    JTextField tf = new JTextField();
                    tf.setBorder(null);
                    tf.setFont(showNameFont);
                    tf.setHorizontalAlignment(JTextField.LEFT);
                    if (i == moneyList.size() - 1) {
                        tf.setText(ownerName + ":\t" + moneyList.get(i) / 100.0 + "元");

                    } else {

                        tf.setText("群成员-" + i + ":\t" + moneyList.get(i) / 100.0 + "元");
                    }
                    showPanel2.add(tf);
                }

                layeredPane.add(show_jsp, JLayeredPane.MODAL_LAYER);
            }

        });

    }

    /* ======================================================================
     * **********************************************************************
     * * 以上代码均为页面部分处理,包括布局/互动/跳转/显示等,大家							*
     * *																	*
     * *																	*
     * **********************************************************************
     * ======================================================================
     */

    /**
     * ownerName : 群主名称
     */
    private String ownerName = "谁谁谁"; // 群主名称
    /**
     * openWay : 红包的类型 [普通红包/手气红包]
     */
    private OpenMode openWay = null;


    /**
     * 构造方法:生成红包界面。
     *
     * @param title 界面的标题
     */

    public RedPacketFrame(String title) {
        super(title);

        // 页面相关的初始化
        init();
    }

    public void setOwnerName(String ownerName) {
        this.ownerName = ownerName;
    }

    public void setOpenWay(OpenMode openWay) {
        this.openWay = openWay;
    }


}
  1. OpenMode :一个接口,包含一个分配方法,用来指定红包类型。
public interface OpenMode {
    /**
     * 请将totalMoney分成count份,保存到ArrayList<Integer>中,返回即可。
     *
     * @param totalMoney            总金额为方便计算,已经转换为整数,单位为分。
     * @param totalCount            红包个数
     * @return ArrayList<Integer>	元素为各个红包的金额值,所有元素的值累和等于总金额。
     */
    ArrayList<Integer> divide(int totalMoney, int totalCount);
}

案例实现
环境搭建:

  1. 创建项目:名称自定义,建议为 RedPacketDemo 。
  2. 导入图片:将 pic 目录,导入项目中,与 src 目录平级。
  3. 导入已知类:在 src 下创建一个包,名字自定义,建议为 known ,将类 RedPacketFrame 、接口 OpenMode 拷入。

代码实现:

  1. 定义 MyRed 类,继承 RedPacketFrame ,代码如下:

public class MyRed extends RedPacketFrame {
    /**
     * 构造方法:生成红包界面。
     *
     * @param title 界面的标题
     */
    public MyRed(String title) {
        super(title);
    }
}

  1. 定义测试类,创建 MyRed对象,代码如下:
public class Bootstrap {

    public static void main(String[] args) {
        MyRed red = new MyRed("微信");
    }

}

运行代码,打开一个发红包的页面。可以输入总金额,红包个数,留言信息。显示效果:

在这里插入图片描述

点击 塞钱进红包 按钮,跳转到下一页面。

在这里插入图片描述
点击 谁谁谁谁 和 開 ,两个区域,发现可以设置两项内容:

  • 谁谁谁谁 :表示群主在发红包,可设置群主名称。通过此方法,熟悉类结构,直接调用父类的方法
    在这里插入图片描述

  • 開 :表示打开红包,跳转到下一页面。但是开启之前,必须先设置红包的类型,否则无法开启。
    在这里插入图片描述
    这些效果RedPackerFrame类里面已经写好的了。

  1. RedPacket 对象,设置群主名称。 setOwnerName(String ownerName) ,是字符串作为参数。我们只需要传递一个字符串即可。
public class Bootstrap {

    public static void main(String[] args) {
    	// 创建红包对象,生成红包界面。
        MyRed red = new MyRed("微信");
       // 设置群主名称
        red.setOwnerName("IT");
    }

}

  1. MyRed对象,设置红包类型。 因为继承窗体类所以可以设置红包类型
    通过MyRed对象的setOpenWay(OpenMode openWay) 把接口作为参数。我们必须定义接口的实现类,重写接口中方法,并传 递实现类对象到 setOpenWay方法中,方可设置完成。再观察接口:
//OpenMode接口
public interface OpenMode {
    /**
     * 请将totalMoney分成count份,保存到ArrayList<Integer>中,返回即可。
     *
     * @param totalMoney            总金额为方便计算,已经转换为整数,单位为分。
     * @param totalCount            红包个数
     * @return ArrayList<Integer>	元素为各个红包的金额值,所有元素的值累和等于总金额。
     */
    ArrayList<Integer> divide(int totalMoney, int totalCount);
}
  1. 普通红包,打开方式 NormalMode,代码如下:
public class NormalMode implements OpenMode {
    @Override
    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
        ArrayList<Integer> list = new ArrayList<Integer>();

        int avg = totalMoney / totalCount; // 平均值
        int mod = totalMoney % totalCount; // 余数,模,零头

        // 注意totalCount - 1代表,最后一个先留着
        for (int i = 0; i < totalCount - 1; i++) {
            list.add(avg);
        }

        // 有零头,需要放在最后一个红包当中
        list.add(avg + mod);

        return list;
    }
}

  1. 发普通红包,代码如下:
public class Bootstrap {

    public static void main(String[] args) {
        //生成红包界面。
        MyRed red = new MyRed("微信");
       // 设置群主名称
        red.setOwnerName("IT");

        //多态形式
        OpenMode normal = new NormalMode();
        // 设置红包类型
        red.setOpenWay(normal);// 普通红包

    }

}
  1. 手气红包【重点】

本质上,手气红包就是把总金额 totalMoney 随机分成指定的 totalCount份,所以必须规定每一份金额的取值范围。如 果范围太小,可能导致后分配红包金额特别大。反之范围太大,可能导致后分配红包金额为0,不够分。可见,取 值范围的定义规则,是手气红包的关键所在。

我们规定:每一份随机金额范围(除最后一份),最小值为1,最大值为当前剩余平均金额的2倍 ,单位为”分”。

举例:总额为50元,发5个红包。:
在这里插入图片描述

小贴士:为方便表格中进行运算,此处,单位为”元”。程序中,建议换算为”分”进行运算

手气红包,打开方式 RandomMode,代码如下:

public class RandomMode implements OpenMode {

    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
        ArrayList<Integer> list = new ArrayList<Integer>();

        // 随机分配,有可能多,有可能少。
        // 最少1分钱,最多不超过“剩下金额平均数的2倍”
        // 第一次发红包,随机范围是0.01元~6.66元
        // 第一次发完之后,剩下的至少是3.34元。
        // 此时还需要再发2个红包
        // 此时的再发范围应该是0.01元~3.34元(取不到右边,剩下0.01)

        // 总结一下,范围的【公式】是:1 + random.nextInt(leftMoney / leftCount * 2);
        Random r = new Random(); // 首先创建一个随机数生成器
        // totalMoney是总金额,totalCount是总份数,不变
        // 额外定义两个变量,分别代表剩下多少钱,剩下多少份
        int leftMoney = totalMoney;
        int leftCount = totalCount;

        // 随机发前n-1个,最后一个不需要随机
        for (int i = 0; i < totalCount - 1; i++) {
            // 按照公式生成随机金额
            int money = r.nextInt(leftMoney / leftCount * 2) + 1;
            list.add(money); // 将一个随机红包放入集合
            leftMoney -= money; // 剩下的金额越发越少
            leftCount--; // 剩下还应该再发的红包个数,递减
        }

        // 最后一个红包不需要随机,直接放进去就得了
        list.add(leftMoney);

        return list;
    }
}

  1. 发手气红包,代码如下:
public class Bootstrap {

    public static void main(String[] args) {
        //生成红包界面。
        MyRed red = new MyRed("微信");
       // 设置群主名称
        red.setOwnerName("IT");


       // 设置红包类型,二选一
       
       /* // 普通红包
        //多态形式
        OpenMode normal = new NormalMode();
        red.setOpenWay(normal);*/
        
        red.setOpenWay(new RandomMode());// 手气红包
    }

}

类的结构:
在这里插入图片描述

展示效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

案例总结
通过 发红包 案例,你都学到了什么呢?请你思考如下问题:

  1. 基础语法,你是否清晰?
  2. 一些基本的类的方法,你是否能够调用?
  3. 案例中哪里体现了继承,继承的作用是什么?
  4. 接口作为参数,如何使用?
  5. 接口作为成员变量,如何使用?
  6. 如何简化接口的使用方式?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83933.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!