看到这个题目,希望大家不要慌忙,从字面上很容易就能看出,我们要用到JFrame框架,然后需要在Frame里面添加雪花,最后让雪花移动起来。

下面我们来具体分析一下:

    首先,我们需要有一个JFrame类,用来加载面板以及运行主方法;

    第二,我们需要有一个JPanel类,将雪花加载上去。

    第三,需要实现雪花的移动。

如何实现这三个功能呢?带着问题,我们来看下面的代码:

MyStarFrame类:

import javax.swing.JFrame;
/**
 * 首先我们来创建JFrame类,这个类我们把它称作MyStarFrame,
 * 包含一个主方法跟两个成员方法。
 * @author ChrisYuan
 *
 */
public class MyStarFrame extends JFrame{
	/*
	 * 定义一个成员变量panel,这里我们使用私有属性,
	 * 为了避免其他类不小心调用它,防止程序出错。
	 * 很多时候,如果我们不知道属性该定义为什么,最好定义为私有。
	 */
	private SnowPanel panel;
	/*
	 * addPanel方法,将另一个类也就是我们提到的panel类加载进来。
	 */
	public void addPanel(){
		panel=new SnowPanel();
		this.add(panel);//使用this加到frame框架里,在这里this代表的是当前的frame对象
		panel.startDown();//调用panel的startDown方法,实现雪从天而降
	}
	/*
	 * showMe方法,其实就是将框架显示出来的方法,里面我们定义了几个常用的方.
	 */
	public void showMe(){
		this.setSize(800,600);//设置框架大小
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//设置框架在点击右上角关闭按钮的时候会从系统中关闭完全关闭退出
		this.setTitle("下雪场景实现");//设置框架标题
		this.setVisible(true);//设置框架可见性为真,让框架显示出来。
	}
	/**
	 * 主方法新建了一个frame对象,然后调用了它的addPanel根showMe方法
	 * @param args
	 */
	public static void main(String[] args) {
		MyStarFrame frame = new MyStarFrame();
		frame.addPanel();
		frame.showMe();
	}
}

import javax.swing.JFrame;
/**
 * 首先我们来创建JFrame类,这个类我们把它称作MyStarFrame,
 * 包含一个主方法跟两个成员方法。
 * @author ChrisYuan
 *
 */
public class MyStarFrame extends JFrame{
	/*
	 * 定义一个成员变量panel,这里我们使用私有属性,
	 * 为了避免其他类不小心调用它,防止程序出错。
	 * 很多时候,如果我们不知道属性该定义为什么,最好定义为私有。
	 */
	private SnowPanel panel;
	/*
	 * addPanel方法,将另一个类也就是我们提到的panel类加载进来。
	 */
	public void addPanel(){
		panel=new SnowPanel();
		this.add(panel);//使用this加到frame框架里,在这里this代表的是当前的frame对象
		panel.startDown();//调用panel的startDown方法,实现雪从天而降
	}
	/*
	 * showMe方法,其实就是将框架显示出来的方法,里面我们定义了几个常用的方.
	 */
	public void showMe(){
		this.setSize(800,600);//设置框架大小
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//设置框架在点击右上角关闭按钮的时候会从系统中关闭完全关闭退出
		this.setTitle("下雪场景实现");//设置框架标题
		this.setVisible(true);//设置框架可见性为真,让框架显示出来。
	}
	/**
	 * 主方法新建了一个frame对象,然后调用了它的addPanel根showMe方法
	 * @param args
	 */
	public static void main(String[] args) {
		MyStarFrame frame = new MyStarFrame();
		frame.addPanel();
		frame.showMe();
	}
}

SnowPanel类:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JPanel;
/**
 * SnowPanel类继承于面板类,里面添加的是下雪动作的具体实现。
 * @author ChrisYuan
 */
public class SnowPanel extends JPanel{
	//定义了两个整型数组,数组里分别存放雪花在面板里的x、y轴的值
	private int[] xx;
	private int[] yy;
	//定义一个字体数组,目的是为了后面定义雪花的时候,让雪花有大小的变化
	private Font[] fs;
	/*
	 * 下面是panel类构造器,在frame类里面new panel对象的时候,
	 * 构造方法里面的属性与方法都会执行或加载。
	 */
	public SnowPanel(){
		xx=new int[300];
		yy=new int[300];
		//循环300次,产生300组随机数,保存在xx和yy数组中
		for (int i = 0; i < xx.length; i++) {
			xx[i]=(int)(Math.random()*800);
			yy[i]=(int)(Math.random()*600);
		}	
		fs=new Font[10];
		//循环10次,产生一种不同大小的字体,保存在fs中
		for(int i=0;i<fs.length;i++){
			fs[i]=new Font("宋体",Font.BOLD,12+i);
		}
	}
	/*
	 * paint方法是JPanel里面的一个方法,可以实现在面板里画东西,
	 * 这里重写了paint方法。
	 */
	public void paint(Graphics g){//g相当于一个画笔,可以画许多东西出来
		super.paint(g);//继承父类的paint方法,也就是继承了paint画的行为
		this.setBackground(Color.BLACK);//这里this代表的是panel
		//意为将panel的背景颜色设置为黑色,Color是java里的颜色类
		g.setColor(Color.WHITE);//将画笔的颜色改为白色
		/*
		 * 循环300次,在面板里画出300颗星星
		 */
		for(int i=0;i<xx.length;i++){
			g.setFont(fs[i%10]);//每十颗星星设置一种不同大小的雪花
			g.drawString("*", xx[i], yy[i]);//画星星
		}
	}
	/*
	 * 创建一个startDown方法,这个方法里面用匿名内部类的方式新建一个线程,
	 * 线程里面实现了,雪花移位并且重画面板的方法。
	 * 这里稍微提一下线程的问题,程序执行的时候,默认是从main方法开始执行,main方法执行后,
	 * 就会变成整个程序的主线程,在这里我们又重新定义了一个新的线程在startDown里,这个线程
	 * 会在main方法调用addPanel的时候加载到内存,主线程不会一直running,系统
	 * 会非配给主线程与startDown里的线程交替运行的时间,这样从宏观来看,两个线程是并发的,也就是
	 * 我们认为的同时在执行。
	 */
	public void startDown(){
		//创建一个新的线程对象,并让他去执行。
		new Thread(){
			//线程执行是将每个点都下移一个位置
			public void run(){//线程,一定要实现他的run方法
				//while死循环里面执行的语句,就是下雪场景不断在更替的动作:坐标位移,重画,睡
				while(true){
					for(int i=0;i<yy.length;i++){
						yy[i]++;
						//判断一下雪花是否超出下边界,如果超出边界,y轴从零开始重新执行下雪
						if(yy[i]>=600){
							yy[i]=0;
						}
					}
					//重画面板动作,意味将位移的雪花重新显现出来
					repaint();
					//异常处理里面的语句是让线程停滞10毫秒,保证雪花下降的速度没有那么快
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}//while end!
			}// run end!
		}.start();//线程要运行,一定要用start方法来开启线程		
	}
}

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JPanel;
/**
 * SnowPanel类继承于面板类,里面添加的是下雪动作的具体实现。
 * @author ChrisYuan
 */
public class SnowPanel extends JPanel{
	//定义了两个整型数组,数组里分别存放雪花在面板里的x、y轴的值
	private int[] xx;
	private int[] yy;
	//定义一个字体数组,目的是为了后面定义雪花的时候,让雪花有大小的变化
	private Font[] fs;
	/*
	 * 下面是panel类构造器,在frame类里面new panel对象的时候,
	 * 构造方法里面的属性与方法都会执行或加载。
	 */
	public SnowPanel(){
		xx=new int[300];
		yy=new int[300];
		//循环300次,产生300组随机数,保存在xx和yy数组中
		for (int i = 0; i < xx.length; i++) {
			xx[i]=(int)(Math.random()*800);
			yy[i]=(int)(Math.random()*600);
		}	
		fs=new Font[10];
		//循环10次,产生一种不同大小的字体,保存在fs中
		for(int i=0;i<fs.length;i++){
			fs[i]=new Font("宋体",Font.BOLD,12+i);
		}
	}
	/*
	 * paint方法是JPanel里面的一个方法,可以实现在面板里画东西,
	 * 这里重写了paint方法。
	 */
	public void paint(Graphics g){//g相当于一个画笔,可以画许多东西出来
		super.paint(g);//继承父类的paint方法,也就是继承了paint画的行为
		this.setBackground(Color.BLACK);//这里this代表的是panel
		//意为将panel的背景颜色设置为黑色,Color是java里的颜色类
		g.setColor(Color.WHITE);//将画笔的颜色改为白色
		/*
		 * 循环300次,在面板里画出300颗星星
		 */
		for(int i=0;i<xx.length;i++){
			g.setFont(fs[i%10]);//每十颗星星设置一种不同大小的雪花
			g.drawString("*", xx[i], yy[i]);//画星星
		}
	}
	/*
	 * 创建一个startDown方法,这个方法里面用匿名内部类的方式新建一个线程,
	 * 线程里面实现了,雪花移位并且重画面板的方法。
	 * 这里稍微提一下线程的问题,程序执行的时候,默认是从main方法开始执行,main方法执行后,
	 * 就会变成整个程序的主线程,在这里我们又重新定义了一个新的线程在startDown里,这个线程
	 * 会在main方法调用addPanel的时候加载到内存,主线程不会一直running,系统
	 * 会非配给主线程与startDown里的线程交替运行的时间,这样从宏观来看,两个线程是并发的,也就是
	 * 我们认为的同时在执行。
	 */
	public void startDown(){
		//创建一个新的线程对象,并让他去执行。
		new Thread(){
			//线程执行是将每个点都下移一个位置
			public void run(){//线程,一定要实现他的run方法
				//while死循环里面执行的语句,就是下雪场景不断在更替的动作:坐标位移,重画,睡
				while(true){
					for(int i=0;i<yy.length;i++){
						yy[i]++;
						//判断一下雪花是否超出下边界,如果超出边界,y轴从零开始重新执行下雪
						if(yy[i]>=600){
							yy[i]=0;
						}
					}
					//重画面板动作,意味将位移的雪花重新显现出来
					repaint();
					//异常处理里面的语句是让线程停滞10毫秒,保证雪花下降的速度没有那么快
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}//while end!
			}// run end!
		}.start();//线程要运行,一定要用start方法来开启线程		
	}
}