Java工程师培训课(十二)

Genius夜光序言:

深邃双眸缓缓闭合.............走过那段多梦的青春岁月,我们的肩上就多了一份责任,思想也更加理性。爱也不再轻浮,而是稳重深沉。

 

正文:

图上的一键优化与垃圾清除同时在运行,在一个进程中同时在执行了多个任务。

假象:

电脑上的程序同时在运行。“多任务”操作系统能同时运行多个进程(程序)——但实际是由于CPU分时机制的作用,使每个进程都能循环获得自己的CPU时间片。但由于轮换速度非常快,使得所有程序好象是在“同时”运行一样。

多线程的好处:

  1. 解决了一个进程里面可以同时运行多个任务(执行路径)。
  2. 提供资源的利用率,而不是提供效率。

多线程的弊端:

  1. 降低了一个进程里面的线程的执行频率。
  2. 对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。
  3. 公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,发生线程安全问题。
  4. 线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。

创建线程的方式一

1、继承Thread类

getName()是获取线程的名字。

执行后的效果:

 

 

问题: 先按照顺序运行完了张三,然后接着再按照顺序运行完李四,我们想要的效果是张三和李四做资源的争夺战,也就是先是张三然后李四,没有顺序的执行。这就证明多线程没有起到效果。

2、需要复写run方法,把要执行的任务放在run方法中。

运行效果:

 

问题: 先按照顺序运行完了张三,然后接着再按照顺序运行完李四,我们想要的效果是张三和李四做资源的争夺战,也就是先是张三然后李四,没有顺序的执行。这就证明多线程没有起到效果。

3、调用start()方法启动线程

效果:

达到了我们预期的效果。

线程的使用细节:

  1. 线程的启动使用父类的start()方法
  2. 如果线程对象直接调用run(),那么JVN不会当作线程来运行,会认为是普通的方法调用。
  3. 线程的启动只能由一次,否则抛出异常
  4. 可以直接创建Thread类的对象并启动该线程,但是如果没有重写run(),什么也不执行。
  5. 匿名内部类的线程实现方式

线程的状态

创建:新创建了一个线程对象

可运行:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取cpu执行权

运行:就绪状态的线程获取了CPU执行权,执行程序代码。

阻临时塞: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态

死亡:线程执行完它的任务时。

常见线程的方法

Thread(String name)     初始化线程的名字

 getName()             返回线程的名字

 setName(String name)    设置线程对象名

 sleep()                 线程睡眠指定的毫秒数。

 getPriority()             返回当前线程对象的优先级   默认线程的优先级是5

 setPriority(int newPriority) 设置线程的优先级    虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。

 currentThread()      返回CPU正在执行的线程的对象

class ThreadDemo1 extends Thread

{

public ThreadDemo1(){

  

}

public ThreadDemo1( String name ){

   super( name );

}

    

public void run(){

   int i = 0;

   while(i < 30){

  i++;

      System.out.println( this.getName() + " "+ " : i = " + i);

  System.out.println( Thread.currentThread().getName() + " "+ " : i = " + i);

  System.out.println( Thread.currentThread() == this );

  System.out.println( "getId()" + " "+ " : id = " + super.getId() );

  System.out.println( "getPriority()" + " "+ " : Priority = " + super.getPriority() );

   }

}

}

class Demo3 

{

public static void main(String[] args)

{

        ThreadDemo1 th1 = new ThreadDemo1("线程1");

ThreadDemo1 th2 = new ThreadDemo1("线程2");

        // 设置线程名

        th1.setName( "th1" );

th2.setName( "th2" );

        // 设置线程优先级  1 ~ 10

th1.setPriority( 10 );

th2.setPriority( 7 );

// 查看SUN定义的线程优先级范围

System.out.println("max : " + Thread.MAX_PRIORITY );

System.out.println("min : " + Thread.MIN_PRIORITY );

        System.out.println("nor : " + Thread.NORM_PRIORITY );

th1.start();

th2.start();

System.out.println("Hello World!");

}

}

 

练习:模拟卖票

 

存在问题:这时候启动了四个线程,那么tickets是一个成员变量,也就是在一个线程对象中都维护了属于自己的tickets属性,那么就总共存在了四份。

解决方案一:tickets使用staitc修饰,使每个线程对象都是共享一份属性。

解决方案2:编写一个类实现Runnable接口。

创建线程的方式二

创建线程的第二种方式.使用Runnable接口.

该类中的代码就是对线程要执行的任务的定义.

1:定义了实现Runnable接口

2:重写Runnable接口中的run方法,就是将线程运行的代码放入在run方法中

3:通过Thread类建立线程对象

4:将Runnable接口的子类对象作为实际参数,传递给Thread类构造方法

5:调用Thread类的start方法开启线程,并调用Runable接口子类run方法

为什么要将Runnable接口的子类对象传递给Thread的构造函数,因为自定义的run方法所属对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法

package cn.itcast.gz.runnable;

public class Demo1 {

public static void main(String[] args) {

MyRun my = new MyRun();

Thread t1 = new Thread(my);

t1.start();

for (int i = 0; i < 200; i++) {

System.out.println("main:" + i);

}

}

}

class MyRun implements Runnable {

public void run() {

for (int i = 0; i < 200; i++) {

System.err.println("MyRun:" + i);

}

}

}

 

 

理解Runnable:

Thread类可以理解为一个工人,而Runnable的实现类的对象就是这个工人的工作(通过构造方法传递).Runnable接口中只有一个方法run方法,该方法中定义的事会被新线程执行的代码.当我们把Runnable的子类对象传递给Thread的构造时,实际上就是让给Thread取得run方法,就是给了Thread一项任务.

 

 

买票例子使用Runnable接口实现

在上面的代码中故意照成线程执行完后,执行Thread.sleep(100),以让cpu让给别的线程,该方法会出现非运行时异常需要处理,这里必须进行try{}catch(){},因为子类不能比父类抛出更多的异常,接口定义中没有异常,实现类也不能抛出异常。

运行发现票号出现了负数,显示了同一张票被卖了4次的情况。

出现了同样的问题。如何解决?

class MyTicket implements Runnable {

int tickets = 100;

public void run() {

while (true) {

if (tickets > 0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "窗口@销售:"

+ tickets + "号票");

tickets--;

 

} else {

System.out.println("票已卖完。。。");

break;

}

}

}

}

public class Demo6 {

public static void main(String[] args) {

MyTicket mt = new MyTicket();

Thread t1 = new Thread(mt);

Thread t2 = new Thread(mt);

Thread t3 = new Thread(mt);

Thread t4 = new Thread(mt);

t1.start();

t2.start();

t3.start();

t4.start();

}

}

锁对象

什么是锁对象?

每个java对象都有一个锁对象.而且只有一把钥匙.

如何创建锁对象:

    可以使用this关键字作为锁对象,也可以使用所在类的字节码文件对应的Class对象作为锁对象

1. 类名.class       

2. 对象.getClass()   

Java中的每个对象都有一个内置锁,只有当对象具有同步方法代码时,内置锁才会起作用,当进入一个同步的非静态方法时,就会自动获得与类的当前实例(this)相关的锁,该类的代码就是正在执行的代码。获得一个对象的锁也成为获取锁、锁定对象也可以称之为监视器来指我们正在获取的锁对象。

因为一个对象只有一个锁,所有如果一个线程获得了这个锁,其他线程就不能获得了,直到这个线程释放(或者返回)锁。也就是说在锁释放之前,任何其他线程都不能进入同步代码(不可以进入该对象的任何同步方法)。释放锁指的是持有该锁的线程退出同步方法,此时,其他线程可以进入该对象上的同步方法。

1:只能同步方法(代码块),不能同步变量或者类

2:每个对象只有一个锁

3:不必同步类中的所有方法,类可以同时具有同步方法和非同步方法

4:如果两个线程要执行一个类中的一个同步方法,并且他们使用的是了类的同一个实例(对象)来调用方法,那么一次只有一个线程能够执行该方法,另一个线程需要等待,直到第一个线程完成方法调用,总结就是:一个线程获得了对象的锁,其他线程不可以进入该对象的同步方法。

5:如果类同时具有同步方法和非同步方法,那么多个线程仍然可以访问该类的非同步方法。

同步会影响性能(甚至死锁),优先考虑同步代码块。

6:如果线程进入sleep() 睡眠状态,该线程会继续持有锁,不会释放。

死锁

经典的“哲学家就餐问题”,5个哲学家吃中餐,坐在圆卓子旁。每人有5根筷子(不是5双),每两个人中间放一根,哲学家时而思考,时而进餐。每个人都需要一双筷子才能吃到东西,吃完后将筷子放回原处继续思考,如果每个人都立刻抓住自己左边的筷子,然后等待右边的筷子空出来,同时又不放下已经拿到的筷子,这样每个人都无法得到1双筷子,无法吃饭都会饿死,这种情况就会产生死锁:每个人都拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源。

当多个线程完成功能需要同时获取多个共享资源的时候可能会导致死锁。

1:两个任务以相反的顺序申请两个锁,死锁就可能出现

2:线程T1获得锁L1,线程T2获得锁L2,然后T1申请获得锁L2,同时T2申请获得锁L1,此时两个线程将要永久阻塞,死锁出现

如果一个类可能发生死锁,那么并不意味着每次都会发生死锁,只是表示有可能。要避免程序中出现死锁。

例如,某个程序需要访问两个文件,当进程中的两个线程分别各锁住了一个文件,那它们都在等待对方解锁另一个文件,而这永远不会发生。

3:要避免死锁

public class DeadLock {

public static void main(String[] args) {

new Thread(new Runnable() { // 创建线程, 代表中国人

public void run() {

synchronized ("刀叉") { // 中国人拿到了刀叉

System.out.println(Thread.currentThread().getName()

+ ": 你不给我筷子, 我就不给你刀叉");

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized ("筷子") {

System.out.println(Thread.currentThread()

.getName() + ": 给你刀叉");

}

}

}

}, "中国人").start();

new Thread(new Runnable() { // 美国人

public void run() {

synchronized ("筷子") { // 美国人拿到了筷子

System.out.println(Thread.currentThread().getName()

+ ": 你先给我刀叉, 我再给你筷子");

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized ("刀叉") {

System.out.println(Thread.currentThread()

.getName() + ": 好吧, 把筷子给你.");

}

}

}

}, "美国人").start();

}

}


线程的通讯

生产者消费者

如果有多个生产者和消费者,一定要使用while循环判断标记,然后在使用notifyAll唤醒,否者容易只用notify容易出现只唤醒本方线程情况,导致程序中的所有线程都在等待。

例如:有一个数据存储空间,划分为两个部分,一部分存储人的姓名,一部分存储性别,我们开启一个线程,不停地想其中存储姓名和性别(生产者),开启另一个线程从数据存储空间中取出数据(消费者)。

由于是多线程的,就需要考虑,假如生产者刚向数据存储空间中添加了一个人名,还没有来得及添加性别,cpu就切换到了消费者的线程,消费者就会将这个人的姓名和上一个人的性别进行了输出。

还有一种情况是生产者生产了若干次数据,消费者才开始取数据,或者消费者取出数据后,没有等到消费者放入新的数据,消费者又重复的取出自己已经去过的数据。

public class Demo10 {

public static void main(String[] args) {

Person p = new Person();

Producer pro = new Producer(p);

Consumer con = new Consumer(p);

Thread t1 = new Thread(pro, "生产者");

Thread t2 = new Thread(con, "消费者");

t1.start();

t2.start();

}

}

 

// 使用Person作为数据存储空间

class Person {

String name;

String gender;

}

 

// 生产者

class Producer implements Runnable {

Person p;

 

public Producer() {

 

}

 

public Producer(Person p) {

this.p = p;

}

 

@Override

public void run() {

int i = 0;

while (true) {

if (i % 2 == 0) {

p.name = "jack";

p.gender = "man";

} else {

p.name = "小丽";

p.gender = "女";

}

i++;

}

 

}

 

}

 

// 消费者

class Consumer implements Runnable {

Person p;

 

public Consumer() {

 

}

 

public Consumer(Person p) {

this.p = p;

}

 

@Override

public void run() {

 

while (true) {

System.out.println("name:" + p.name + "---gnder:" + p.gender);

}

}

 

}

 

在上述代码中,Producer和Consumer 类的内部都维护了一个Person类型的p成员变量,通过构造函数进行赋值,在man方法中创建了一个Person对象,将其同时传递给Producer和Consumer对象,所以Producer和Consumer访问的是同一个Person对象。并启动了两个线程。

 

输出:

显然屏幕输出了小丽 man 这样的结果是出现了线程安全问题。所以需要使用synchronized来解决该问题。

 

package cn.itcast.gz.runnable;

 

public class Demo10 {

public static void main(String[] args) {

Person p = new Person();

Producer pro = new Producer(p);

Consumer con = new Consumer(p);

Thread t1 = new Thread(pro, "生产者");

Thread t2 = new Thread(con, "消费者");

t1.start();

t2.start();

}

}

 

// 使用Person作为数据存储空间

class Person {

String name;

String gender;

}

 

// 生产者

class Producer implements Runnable {

Person p;

 

public Producer() {

 

}

 

public Producer(Person p) {

this.p = p;

}

 

@Override

public void run() {

int i = 0;

while (true) {

synchronized (p) {

if (i % 2 == 0) {

p.name = "jack";

p.gender = "man";

} else {

p.name = "小丽";

p.gender = "女";

}

i++;

}

 

}

 

}

 

}

 

// 消费者

class Consumer implements Runnable {

Person p;

 

public Consumer() {

 

}

 

public Consumer(Person p) {

this.p = p;

}

 

@Override

public void run() {

 

while (true) {

synchronized (p) {

System.out.println("name:" + p.name + "---gnder:" + p.gender);

}

 

}

}

 

}

 

编译运行:屏幕没有再输出jack –女  或者小丽- man 这种情况了。说明我们解决了线程同步问题,但是仔细观察,生产者生产了若干次数据,消费者才开始取数据,或者消费者取出数据后,没有等到消费者放入新的数据,消费者又重复的取出自己已经去过的数据。这个问题依然存在。

升级:在Person类中添加两个方法,set和read方法并设置为synchronized的,让生产者和消费者调用这两个方法。

public class Demo10 {

public static void main(String[] args) {

Person p = new Person();

Producer pro = new Producer(p);

Consumer con = new Consumer(p);

Thread t1 = new Thread(pro, "生产者");

Thread t2 = new Thread(con, "消费者");

t1.start();

t2.start();

}

}

 

// 使用Person作为数据存储空间

class Person {

String name;

String gender;

 

 

public synchronized void set(String name, String gender) {

this.name = name;

this.gender = gender;

}

 

public synchronized void read() {

System.out.println("name:" + this.name + "----gender:" + this.gender);

}

 

}

 

// 生产者

class Producer implements Runnable {

Person p;

 

public Producer() {

 

}

 

public Producer(Person p) {

this.p = p;

}

 

@Override

public void run() {

int i = 0;

while (true) {

 

if (i % 2 == 0) {

p.set("jack", "man");

} else {

p.set("小丽", "女");

}

i++;

 

}

 

}

 

}

 

// 消费者

class Consumer implements Runnable {

Person p;

 

public Consumer() {

 

}

 

public Consumer(Person p) {

this.p = p;

}

 

@Override

public void run() {

 

while (true) {

p.read();

 

}

}

 

}

 

 

需求:我们需要生产者生产一次,消费者就消费一次。然后这样有序的循环。

这就需要使用线程间的通信了。Java通过Object类的wait,notify,notifyAll这几个方法实现线程间的通信。

      1. 等待唤醒机制

wait:告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。

notify:唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。注意:被唤醒的线程是进入了可运行状态。等待cpu执行权。

notifyAll:唤醒持有同一监视器中调用wait的所有的线程。

 

如何解决生产者和消费者的问题?

可以通过设置一个标记,表示数据的(存储空间的状态)例如,当消费者读取了(消费了一次)一次数据之后可以将标记改为false,当生产者生产了一个数据,将标记改为true。

,也就是只有标记为true的时候,消费者才能取走数据,标记为false时候生产者才生产数据。

代码实现:

package cn.itcast.gz.runnable;

 

public class Demo10 {

public static void main(String[] args) {

Person p = new Person();

Producer pro = new Producer(p);

Consumer con = new Consumer(p);

Thread t1 = new Thread(pro, "生产者");

Thread t2 = new Thread(con, "消费者");

t1.start();

t2.start();

}

}

 

// 使用Person作为数据存储空间

class Person {

String name;

String gender;

boolean flag = false;

 

public synchronized void set(String name, String gender) {

if (flag) {

try {

wait();

} catch (InterruptedException e) {

 

e.printStackTrace();

}

}

this.name = name;

this.gender = gender;

flag = true;

notify();

}

 

public synchronized void read() {

if (!flag) {

try {

wait();

} catch (InterruptedException e) {

 

e.printStackTrace();

}

}

System.out.println("name:" + this.name + "----gender:" + this.gender);

flag = false;

notify();

}

 

}

 

// 生产者

class Producer implements Runnable {

Person p;

 

public Producer() {

 

}

 

public Producer(Person p) {

this.p = p;

}

 

@Override

public void run() {

int i = 0;

while (true) {

 

if (i % 2 == 0) {

p.set("jack", "man");

} else {

p.set("小丽", "女");

}

i++;

 

}

 

}

 

}

 

// 消费者

class Consumer implements Runnable {

Person p;

 

public Consumer() {

 

}

 

public Consumer(Person p) {

this.p = p;

}

 

@Override

public void run() {

 

while (true) {

p.read();

 

}

}

 

}

 

 

线程间通信其实就是多个线程在操作同一个资源,但操作动作不同,wait,notify(),notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。

为什么这些方法定义在Object类中

因为这些方法在操作线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被统一锁上notify唤醒,不可以对不同锁中的线程进行唤醒,就是等待和唤醒必须是同一个锁。而锁由于可以使任意对象,所以可以被任意对象调用的方法定义在Object类中

wait() 和 sleep()有什么区别?

wait():释放资源,释放锁。是Object的方法

sleep():释放资源,不释放锁。是Thread的方法

定义了notify为什么还要定义notifyAll,因为只用notify容易出现只唤醒本方线程情况,导致程序中的所有线程都在等待。

 

  1. 线程生命周期

任何事物都是生命周期,线程也是,

1. 正常终止  当线程的run()执行完毕,线程死亡。

2. 使用标记停止线程

注意:Stop方法已过时,就不能再使用这个方法。

如何使用标记停止线程停止线程。

开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,线程就结束。

 

class StopThread implements Runnable {

public boolean tag = true;

@Override

public void run() {

int i = 0;

 

while (tag) {

i++;

System.out.println(Thread.currentThread().getName() + "i:" + i);

}

}

}

public class Demo8 {

public static void main(String[] args) {

StopThread st = new StopThread();

Thread th = new Thread(st, "线程1");

th.start();

for (int i = 0; i < 100; i++) {

if (i == 50) {

System.out.println("main i:" + i);

st.tag = false;

}

}

}

}

上述案例中定义了一个计数器i,用来控制main方法(主线程)的循环打印次数,在i到50这段时间内,两个线程交替执行,当计数器变为50,程序将标记改为false,也就是终止了线程1的while循环,run方法结束,线程1也随之结束。注意:当计数器i变为50的,将标记改为false的时候,cpu不一定马上回到线程1,所以线程1并不会马上终止。

  1. 后台线程

后台线程:就是隐藏起来一直在默默运行的线程,直到进程结束。

 实现:

      setDaemon(boolean on)

 特点:

当所有的非后台线程结束时,程序也就终止了同时还会杀死进程中的所有后台线程,也就是说,只要有非后台线程还在运行,程序就不会终止,执行main方法的主线程就是一个非后台线程。

必须在启动线程之前(调用start方法之前)调用setDaemon(true)方法,才可以把该线程设置为后台线程。

一旦main()执行完毕,那么程序就会终止,JVM也就退出了。

可以使用isDaemon() 测试该线程是否为后台线程(守护线程)。

该案例:开启了一个qq检测升级的后台线程,通过while真循环进行不停检测,当计数器变为100的时候,表示检测完毕,提示是否更新,线程同时结束。

为了验证,当非后台线程结束时,后台线程是否终止,故意让该后台线程睡眠一会。发现只要main线程执行完毕,后台线程也就随之消亡了。

class QQUpdate implements Runnable {

int i = 0;

 

@Override

public void run() {

while (true) {

 

System.out.println(Thread.currentThread().getName() + " 检测是否有可用更新");

i++;

try {

Thread.sleep(10);

} catch (InterruptedException e) {

 

e.printStackTrace();

}

if (i == 100) {

System.out.println("有可用更新,是否升级?");

break;

}

}

}

}

public class Demo9 {

public static void main(String[] args) {

QQUpdate qq = new QQUpdate();

Thread th = new Thread(qq, "qqupdate");

th.setDaemon(true);

th.start();

System.out.println(th.isDaemon());

System.out.println("hello world");

}

}

     

Thread的join方法

当A线程执行到了B线程Join方法时A就会等待,等B线程都执行完A才会执行,Join可以用来临时加入线程执行

本案例,启动了一个JoinThread线程,main(主线程)进行for循环,当计数器为50时,让JoinThread,通过join方法,加入到主线程中,发现只有JoinThread线程执行完,主线程才会执行完毕.

可以刻意让JoinThread线程sleep,如果JoinThread没有调用join方法,那么肯定是主线程执行完毕,但是由于JoinThread线程加入到了main线程,必须等JoinThread执行完毕主线程才能继续执行。

class JoinThread implements Runnable {

 

@Override

public void run() {

int i = 0;

while (i < 300) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + " i:" + i);

i++;

}

}

}

 

public class Demo10 {

public static void main(String[] args) throws InterruptedException {

JoinThread jt = new JoinThread();

Thread th = new Thread(jt, "one");

th.start();

int i = 0;

while (i < 200) {

if (i == 100) {

th.join();

}

System.err.println(Thread.currentThread().getName() + " i:" + i);

i++;

 

}

}

}

上述程序用到了Thread类中的join方法,即th.join语句,作用是将th对应的线程合并到嗲用th.join语句的线程中,main方法的线程中计数器到达100之前,main线程和one线程是交替执行的。在main线程中的计数器到达100后,只有one线程执行,也就是one线程此时被加进了mian线程中,one线程不执行完,main线程会一直等待

带参数的join方法是指定合并时间,有纳秒和毫秒级别。

 

 

 

休息一下~~

GUI编程引言【推荐一款软件netbeans】

以前的学习当中,我们都使用的是命令交互方式:

例如:在DOS命令行中通过javac java命令启动程序.

软件的交互的方式:

1. 命令交互方式   

图书管理系统

2. 图形交互方式

Java提供了专业的API用于开发图形用户界面

GUI--> Graphic  User   Interface

将要了解GUI API的框架结构,以及GUI组件以及组件之间的关系,容器和布局管理器,颜色,字体等.

  1. GUI的分类

    1. AWT      

Java1.0版本的图形用户界面库,设计目标是帮助程序员编写在所有平台上都能良好表现的GUI程序。为了实现这个目标Java1.0提供了抽象窗口工具集(AWT),但是这个目标并没有达到。AWT在所有的系统上表现都不好。因为:最初版本的AWT是在一个月内构思,设计和实现的(Think in Java )。

Abstract  Window  Toolkit   抽象窗口工具集

Java将图形用户界面相关的类捆绑在了一起,放在了一个称之为抽象窗口工具集的库中.AWT适合开发简单的图形用户界面,并不适合开发复杂的GUI项目.

位于: java.awt.* 中,定义了很多的组件类,开发者可以直接创建对象加以使用

缺点: 所有的图形界面都依赖于底层的操作系统,容易发生于特定平台相关的故障.

AWT调用本地系统资源生成图形化界面, 依赖本地平台. 1.0

 

 

Swing

SUN公司对AWT进行了升级,基于AWT,推出了一种更稳定,更通用和更灵活的库.称之为Swing组件库(Swing component)。

既然都是用于GUI设计的组件库,那么为了区分Swing组件类和对应的AWT组件类,Swing组件类都已字母J为前缀.位于:javax.swing.* 中,提供了和AWT中等同的所有的组件类,但是类名的前面多加了一个J.

SWING可以跨平台. 1.2

我们主要学习Swing GUI组件.

Java GUI API

GUI API包含的类分为三个部分:组件类(component class) 容器类(container class),和辅助类(helper class)

  1. 组件类是用来创建用户图形界面的,例如JButton,JLabel,JTextField.
  2. 容器类是用来包含其他组件的,例如JFrame,JPanel
  3. 辅助类是用来支持GUI组件的,例如Color,Font

组件类

在图形用户界面程序中当我们想要创建按钮、复选框和滚动条等这些可以显示在屏幕上的对象,该如何创建。其实这些都属于一类叫做组件类。

AWT中的组件根类

类 Component

java.lang.Object

                  java.awt.Component

Swing中的组件根类

javax.swing

类 JComponent

java.lang.Object

                  java.awt.Component

                        java.awt.Container

                              javax.swing.JComponent

 

组件类的实例可以显示在屏幕上.Component类是包括容器类的所有用户界面类的根类是java.awt中的类,对应的Swing中的是Jcomponent.了解了Component和JComponent都是抽象类.所以不能使用new关键字创建对象.所以需要使用它们的具体的实现类来创建对象.

在AWT中典型图形用户界面中的按钮(Button)、复选框(Checkbox)和滚动条(Scrollbar)都是组件类,都是Component类的子类.

在Swing中的GUI组件,有对应的JButton,JCheckBox,JscrollBar

继承关系图(AWT)

​​​​​​​容器类

 

容器(Container),是一个特殊的组件,该组件可以通过add()添加其他组件。

容器类适用于盛装其他GUI组件的GUI组件.例如 Panel Frame Dialog都是AWT组件的容器类.对应的Swing组件的容器类是JPanel JFrame JDialog

​​​​​​​GUI辅助类

用来描述GUI组件的属性,例如图像的颜色,字体等. 注意:辅助类是在java.awt中的

​​​​​​​GUI运行原理

在JDK的bin目录中有javaw.exe .javaw.exe是java在window中专门用于执行GUI程序.

体验GUI

 

import javax.swing.JFrame;

import javax.swing.JOptionPane;

 

public class Demo {

public static void main(String[] args) {

// 创建JFrame

JFrame frame = new JFrame("hello,world");

// 设置尺寸

frame.setSize(200, 100);

// JFrame在屏幕居中

frame.setLocationRelativeTo(null);

// JFrame关闭时的操作

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// 显示JFrame

frame.setVisible(true);

 

}

}

 

 

Jframe框架

 

JFrame(框架)是一个容器

创建一个用户界面需要创建一个JFrame来存放用户界面组件.例如存放按钮,文本框。

javax.swing

类 JFrame

java.lang.Object

  java.awt.Component

      java.awt.Container

          java.awt.Window

              java.awt.Frame

                  javax.swing.JFrame

 

​​​​​​​创建一个框架

 

public class Demo1 {

public static void main(String[] args) {

// 创建JFrame

JFrame frame = new JFrame("我的frame");

// 显示JFrame

frame.setVisible(true);

 

}

}

注意:需要调用setVisible(true)方法后才会显示框架

运行程序会在窗口的左上角显示一个窗口,但是只能显示标题栏,而且关闭JFrame时,程序程序没有停止.

所以需要完成如下需求:

1: 设置Jframe的宽度和高度

2: 让Jframe显示在屏幕中间

3: 关闭Jframe时,程序会停止

 

​​​​​​​设置JFrame的宽度和高度

java.awt.Window.setSize(int width, int height)

查找API文档,查看 setSize方法,可以指定框架的宽度和高度.参数类型是int,注意是以像素为单位,普通的笔记本的屏幕分辨率为1280*800或者1366*768.注意:分辨率表示每平方英寸的像素数.屏幕分辨率越高,屏幕的像素越多.所以分辨率越高看到的细节就越多.

setSize方法被定义在java.awt.Component类中,被Component  的子类java.awt.Window 重写.而Jframe以继承了Windiws类所以也具备该方法.

​​​​​​​JFrame显示在屏幕中间

setLocationRelativeTo()方法

java.awt.Window.setLocationRelativeTo(Component c)

设置窗口相对于指定组件的位置。

如果 c 为 null,则此窗口将置于屏幕的中央。

setLocationRelativeTo(null)方法可以在屏幕上居中显示框架.

 

如果不想要设置在中间,可以使用

setLocation(200, 100);

 

​​​​​​​关闭JFrame程序停止

setDefaultCloseOperation(int operation)方法

javax.swing.JFrame.setDefaultCloseOperation(int operation)

 

该方法告诉程序,当框架关闭时结束程序.方法的参数是JFrame 的常量EXIT_ON_CLOSE

 添加完毕

public class Demo1 {

public static void main(String[] args) {

// 创建JFrame

JFrame frame = new JFrame("我的frame");

// 设置尺寸

frame.setSize(200, 100);

// JFrame在屏幕居中

frame.setLocationRelativeTo(null);

// JFrame关闭时的操作

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// 显示JFrame

frame.setVisible(true);

 

}

}

​​​​​​​框架中添加组件

上述案例中的框架是空的.可以通过add方法在框架中添加组件

java.awt.Container.add(Component comp)

 

代码:

public static void main(String[] args) {

// 创建JFrame

JFrame frame = new JFrame("我的frame");

 

// 创建按钮

JButton button = new JButton("OK");

// 向frame中添加一个按钮

frame.add(button);

 

// 设置尺寸

frame.setSize(200, 100);

// JFrame在屏幕居中

frame.setLocationRelativeTo(null);

// JFrame关闭时的操作

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// 显示JFrame

frame.setVisible(true);

 

}

 

 

运行程序,会显示上图所示窗口.调整窗口的大小,按钮都是显示在窗口的中央,并且占满整个框架.这是因为组件(本例就是按钮)是被布局管理器放到框架中的.默认布局管理器就是将按钮放到中央.

 

备注: 可以通过f.setVisible(false);  隐藏窗体f.dispose();关闭窗口

  设置图片:

  setIconImage(Toolkit.getDefaultToolkit().createImage("png-0015.png"));

JoptionPane对话框

显示一个带有OK 按钮的模态对话框。

下面是几个使用showMessageDialog 的例子:

Java代码  

JOptionPane.showMessageDialog(null, "错误信息提示", "标题",

JOptionPane.INFORMATION_MESSAGE); 效果如下:

Java代码  

  1. JOptionPane.showMessageDialog(jPanel, "提示消息""标题",JOptionPane.WARNING_MESSAGE);  

 效果如下:

Java代码  

  1. JOptionPane.showMessageDialog(null"提示消息.""标题",JOptionPane.ERROR_MESSAGE);  

Java代码  

  1. JOptionPane.showMessageDialog(null"提示消息.""标题",JOptionPane.PLAIN_MESSAGE);  

1.2 showOptionDialog

这个函数可以改变显示在按钮上的文字。你还可以执行更多的个性化操作。

常规的消息框:

Java代码  

  1. int n = JOptionPane.showConfirmDialog(null"你高兴吗?""标题",JOptionPane.YES_NO_OPTION);//i=0/1  

 效果如下:

输入框 :

String inputValue = JOptionPane.showInputDialog("请输入你给我金额");

 

面板(Panel)

面板也是一个容器的组件,可以在上面添加

 

注意:面板不能单独使用,必须在顶层窗口中使用。

 

常见组件

一些常用的组件例如:

JLabel,

JButton ,

JTextField

JPassworldField

JRadioButton

JCheckBox

JTextArea

JList

JMenuBar

JMenu

JMenultem

 

​​​​​​​Button按钮

Java中的Swing提供了常规按钮,单选按钮,复选按钮和菜单按钮​​​​​​​

 

JButtion普通按钮

按钮Button是点击时触发动作事件的组件.

 

​​​​​​​JRadioButton单选按钮

单选按钮,可以让用户从一组选项中选择一个单一条目.例如性别.

使用单选按钮时注意将,单选按钮放在一组,需要使用java.swing.ButtonGroup. 的add方法,添加到一个组中,位于同一个组的单选按钮就是互斥的. 如果没有将单选按钮放在一个组中,就是独立的..我们让然需要把按钮添加在容器中.因为ButtonGroup添加到容器中..

​​​​​​​JCheckBox复选框

多选

    1. JLabel标签
    2. JTextField文本域
    3. JTextArea文本区域
    4. JComboBox组合框
    5. JList 列表框

例如: String[] data = { "one", "two", "three" };

JList list = new JList(data);

p1.add(list);

​​​​​​​JmenuBar菜单条

JMenu菜单

JMenultem菜单项

菜单条(MenuBar)中包含菜单(Menu),菜单中中包含菜单项(MenuItem)

注意添加的顺序。例如:记事本的菜单条中包含文件、编辑、格式、查看、帮助菜单。其中文件菜单中包含新建、打开、保存、另存为等菜单项

案例综合。

public class CommonComponent extends JFrame {

public CommonComponent() {

 

// 面板

JPanel p1 = new JPanel();

add(p1);

 

// 标签

JLabel name = new JLabel("用户名:");

p1.add(name);

 

// 文本域

JTextField field = new JTextField(8);

p1.add(field);

 

// 标签

JLabel passwd = new JLabel("密码");

p1.add(passwd);

// 密码域

JPasswordField pass = new JPasswordField(8);

p1.add(pass);

 

// 单选按钮

JLabel gender = new JLabel("性别");

p1.add(gender);

JRadioButton male = new JRadioButton("男");

JRadioButton female = new JRadioButton("女");

// 单选按钮组,同一个单选按钮组的互斥.

ButtonGroup group = new ButtonGroup();

group.add(male);

group.add(female);

// 注意,单选按钮组不能添加进容器

p1.add(male);

p1.add(female);

 

// 复选框

JLabel like = new JLabel("爱好:");

p1.add(like);

JCheckBox eat = new JCheckBox("吃饭");

JCheckBox movie = new JCheckBox("看电影");

JCheckBox sleep = new JCheckBox("睡觉");

p1.add(eat);

p1.add(movie);

p1.add(sleep);

 

// 文本域

JLabel info = new JLabel("个人简介");

p1.add(info);

JTextArea area = new JTextArea(20, 20);

p1.add(area);

 

// 列表

String[] data = { "one", "two", "three" };

JList list = new JList(data);

p1.add(list);

 

// 普通按钮

JButton button = new JButton("注册");

p1.add(button);

 

// 菜单条

JMenuBar bar = new JMenuBar();

// 菜单

JMenu menu = new JMenu("文件");

// 菜单选项

JMenuItem myNew = new JMenuItem("新建");

JMenuItem myOpen = new JMenuItem("打开");

bar.add(menu);

menu.add(myNew);

menu.add(myOpen);

add(bar, BorderLayout.NORTH);

 

}

 

public static void main(String[] args) {

CommonComponent frame = new CommonComponent();

frame.setTitle("常用组件");

frame.setSize(400, 400);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// 自适应

frame.pack();

frame.setVisible(true);

}

}

 

 

样式如下:

因为面板默认是流式布局.

 

 

布局管理器

Java的GUI组件都放置在容器中,他们的位置是由容器的布局管理器来管理的.在前面的程序中,并没有指定将OK按钮放置在框架的什么位置,但是,Java知道应该把它放置在哪里,因为在后台工作的布局管理器能够将组件放到正确的位置.布局管理器是使用布局管理器类创建的.

我们可以使用setLayout() 方法在容器中设置布局管理器.

我们将要了解FlowLayout GridLayout BorderLayout

​​​​​​​FlowLayout流式布局

FlowLayout(流式布局)是最简单布局管理器. Jpanel容器默认的布局管理器  

流式布局,按照组件添加的顺序,从左到到右将组件排列在容器中.当放满一行,就开始新的一行.在FlowLayout有3个常量FlowLayout可以指定组件的对齐方式.

LEFT   每一行组件都应该是左对齐的

RIGHT  每一行组件都应该是右对齐的

CENTER  每一行组件都应该是居中的

还可以指定组件之间的以像素为单位的间隔.

int getHgap()

          获取组件之间以及组件与 Container 的边之间的水平间隙。

int getVgap()

          获取组件之间以及组件与 Container 的边之间的垂直间隙。

void setHgap(int hgap)

          设置组件之间以及组件与 Container 的边之间的水平间隙。

void setVgap(int vgap)

          设置组件之间以及组件与 Container 的边之间的垂直间隙。

 

这个布局管理器的对其方式默认值是CENTER  

这个布局管理器的水平间隔默认值是 5个像素

这个布局管理器的垂直间隔默认是是5个像素

创建该布局管理器

FlowLayout()

          构造一个新的 FlowLayout,它是居中对齐的,默认的水平和垂直间隙是 5 个单位。

FlowLayout(int align)

          构造一个新的 FlowLayout,它具有指定的对齐方式,默认的水平和垂直间隙是 5 个单位。

FlowLayout(int align, int hgap, int vgap)

          创建一个新的流布局管理器,它具有指定的对齐方式以及指定的水平和垂直间隙。

 

 

案例:创建框架,使用流失布局管理器.向该框架添加三个标签和文本域.

public class ShowFlowLayout extends JFrame {

 

public ShowFlowLayout() {

super.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 20));

 

add(new JLabel("姓名:"));

add(new JTextField(8));

add(new JLabel("邮箱:"));

add(new JTextField(8));

add(new JLabel("电话:"));

add(new JTextField(8));

 

}

 

public static void main(String[] args) {

ShowFlowLayout frame = new ShowFlowLayout();

frame.setTitle("FlowLayout");

frame.setSize(500, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

该案例在本类main方法中创建了一个本类对象. 该类的构造函数中创建并且添加组件.

该案例使用了FlowLayout管理器在框架放置组件.如果改变框架的大小.组建会自动的重新排列,以适应框架.

 

例如:

如果将同一个按钮在框架中添加10次,那么该框架只会出现一次,将一个按钮向容器中添加多以和一次是一样的.

 

 

​​​​​​​GridLayout网格布局

GridLyaout是以网格形式管理组件的.组件按照他们添加的顺序从左到右排列,显示第一行,接着是第二行,一次类推.

Gridlayout可以指定网格中的行数和列数

规则如下:

行数和列数可以是0但是不能两者都为0.

如果一个为0另外一个不为0,那么不为0的行或列的大小就是固定的,为0的行或者列有布局管理器动态决定.

例如:如果指定一个网格有0行3列10个组件,GirdLayout会创建3个固定的列和行,最后一行只有一个组件.如果指定一个网格有3行0列10个组件,GridLayout就会创建3行4列,最后一行包含2个组件.

如果行数和列数都不为0,那么以行数为依据.所以行数是固定的,布局管理器会动态的计算列数.例如,如果指定一个网格有3行3列10个组件,GridLayout会创建3个固定的行和4列,最后一行包含2个组件.

 

构造方法

GridLayout()

          创建具有默认值的网格布局,即每个组件占据一行一列。

GridLayout(int rows, int cols)

          创建具有指定行数和列数的网格布局。

GridLayout(int rows, int cols, int hgap, int vgap)

          创建具有指定行数和列数,水平间隔,垂直间隔的网格布局。

 

方法:

int getRows()

          获取此布局中的行数。 默认值是1

int getColumns()

          获取此布局中的列数。 默认值是1

 int getHgap()

          获取组件之间的水平间距。 默认值是0

int getVgap()

          获取组件之间的垂直间距。默认值是0

 

设置

void setRows(int rows)

          将此布局中的行数设置为指定值。默认值是1

void setColumns(int cols)

          将此布局中的列数设置为指定值。  默认值是1

 void setHgap(int hgap)

          将组件之间的水平间距设置为指定值。  默认值是0

 void setVgap(int vgap)

          将组件之间的垂直间距设置为指定值。默认值是0

 

 

案例:该案例依然添加3个标签和3个文本域,只不过布局管理器是GrigLayout

public class ShowGridLayout extends JFrame {

public ShowGridLayout() {

setLayout(new GridLayout(3, 2, 5, 5));

 

add(new JLabel("姓名:"));

add(new JTextField(8));

add(new JLabel("邮箱:"));

add(new JTextField(8));

add(new JLabel("电话:"));

add(new JTextField(8));

}

 

public static void main(String[] args) {

ShowGridLayout frame = new ShowGridLayout();

frame.setTitle("GridLayout");

frame.setSize(200, 125);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

 

例如

:

如果使用setLayout(new GridLayout(3,10))替换setlayout语句,还是会得到3行2列,因为行的参数非零,所以列的参数会被忽略.列的实际参数是由布局管理器计算出来的.

​​​​​​​BorderLayout边框布局

边框布局,JFrame 容器默认的布局管理器是边框布局.该管理器将容器非为东西南北中5个区域.我们使用add(Component,index)方法可以将组件添加进到BorderLayout中,index是一个常量,有5个值

EAST

          东区域的布局约束(容器右边)。

WEST

          西区域的布局约束(容器左边)。

SOUTH

          南区域的布局约束(容器底部)。

NORTH

          北区域的布局约束(容器顶部)。

CENTER

          中间区域的布局约束(容器中央)。

 

构造函数

BorderLayout()

          构造一个组件之间没有间距的新边框布局。

BorderLayout(int hgap, int vgap)

          构造一个具有指定组件间距的边框布局。

 

 

方法:

int getHgap()

          返回组件之间的水平间距。

int getVgap()

          返回组件之间的垂直间距。

void setHgap(int hgap)

          设置组件之间的水平间距。 默认值是0

 void setVgap(int vgap)

          设置组件之间的垂直间距。默认值是0

 

 

组件会根据他们最合适的尺寸和在容器中的位置来放置,南北组件可以水平拉伸,东西组件可以垂直拉伸,中央组件既可以水平拉伸也可以垂直拉伸.

案例:演示边框布局管理器

public class ShowBorderLayout extends JFrame {

public ShowBorderLayout() {

setLayout(new BorderLayout(5, 10));

 

add(new JButton("东"), BorderLayout.WEST);

add(new JButton("西"), BorderLayout.EAST);

add(new JButton("南"), BorderLayout.SOUTH);

add(new JButton("北"), BorderLayout.NORTH);

add(new JButton("中"), BorderLayout.CENTER);

}

 

public static void main(String[] args) {

ShowBorderLayout frame = new ShowBorderLayout();

frame.setTitle("BorderLayout");

frame.setSize(300, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

注意: 如果布局管理器为Border管理器,调用add方法,没有说明组件的位置(东西南北中)默认是center.

所以

add(new JButton("ok"))add(new JButton("ok"), BorderLayout.CENTER )

效果是一样的.

如果在Border容器中添加2个组件,都没有指定位置,那么只会显示最后一个组件

add(new JButton("ok1"))和add(new JButton("ok2")) 只会显示ok2

 

使用布局管理器

代码:

public class CommonComponent extends JFrame {

public CommonComponent() {

// 菜单条

JMenuBar bar = new JMenuBar();

// 菜单

JMenu menu = new JMenu("文件");

// 菜单选项

JMenuItem myNew = new JMenuItem("新建");

JMenuItem myOpen = new JMenuItem("打开");

bar.add(menu);

menu.add(myNew);

menu.add(myOpen);

add(bar, BorderLayout.NORTH);

 

// 面板

JPanel p1 = new JPanel();

p1.setLayout(new GridLayout(2, 2, 5, 5));

add(p1);

 

// 标签

JLabel name = new JLabel("用户名:");

p1.add(name);

 

// 文本域

JTextField field = new JTextField(8);

p1.add(field);

 

// 标签

JLabel passwd = new JLabel("密码");

p1.add(passwd);

// 密码域

JPasswordField pass = new JPasswordField(8);

p1.add(pass);

 

JPanel p2 = new JPanel();

// 单选按钮

JLabel gender = new JLabel("性别");

p2.add(gender);

JRadioButton male = new JRadioButton("男");

JRadioButton female = new JRadioButton("女");

// 单选按钮组,同一个单选按钮组的互斥.

ButtonGroup group = new ButtonGroup();

group.add(male);

group.add(female);

// 注意,单选按钮组不能添加进容器

p2.add(male);

p2.add(female);

 

JPanel p3 = new JPanel();

// 复选框

JLabel like = new JLabel("爱好:");

p3.add(like);

JCheckBox eat = new JCheckBox("吃饭");

JCheckBox movie = new JCheckBox("看电影");

JCheckBox sleep = new JCheckBox("睡觉");

p3.add(eat);

p3.add(movie);

p3.add(sleep);

 

JPanel p4 = new JPanel(new GridLayout(1, 0, 5, 5));

// 文本域

JLabel info = new JLabel("个人简介:");

p4.add(info);

JTextArea area = new JTextArea(50, 10);

p4.add(area);

 

JPanel p5 = new JPanel(new BorderLayout());

// 列表

String[] data = { "one", "two", "three" };

JList list = new JList(data);

p5.add(list, BorderLayout.WEST);

 

JPanel p6 = new JPanel();

// 普通按钮

JButton button = new JButton("注册");

p6.add(button);

JButton button2 = new JButton("取消");

p6.add(button2);

 

setLayout(new GridLayout(7, 1, 5, 5));

add(p1);

add(p2);

add(p3);

add(p4);

add(p5);

add(p6);

 

}

 

public static void main(String[] args) {

CommonComponent frame = new CommonComponent();

frame.setTitle("常用组件");

frame.setSize(400, 600);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// 自适应

// frame.pack();

frame.setVisible(true);

}

}

格式如下:

需求:需要在一个Jframe框架中放置10个按钮和一个文本域.

要求按钮以网格形式放置,文本域单独一行.

如果将这些组件放在一个单独的容器中,很难达要去的效果.我们使用Java图形用户界面进行程序设计时,可以将一个窗口分成几个面板Jpanel.面板的作用就是分组放置用户界面的子容器.这里可以将按钮添加到一个面板中,然后经面板添加到框架中.

在Swing中的面板是JPanel 可以使用new Jpanel () 创建一个默认是FlowLayout管理器的面板,也可以使用newJPanel(LayoutManager) 创建一个带特定布局管理器的面板.使用add方法将组件添加进面板中.

例如:

public class ShowJPanel extends JFrame {

ShowJPanel() {

JButton button = new JButton("ok");

JPanel jPanel = new JPanel();

jPanel.add(button);

add(jPanel);

}

 

public static void main(String[] args) {

ShowJPanel frame = new ShowJPanel();

frame.setTitle("面板");

frame.setSize(200, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

 

图示:

 

案例:

public class TestPanel extends JFrame {

public TestPanel() {

// 创建面板,默认是流式布局

JPanel p1 = new JPanel();

// 指定为网格布局,4行3列

p1.setLayout(new GridLayout(4, 3));

for (int i = 1; i <=9; i++) {

p1.add(new JButton("" + i));

}

p1.add(new JButton("0"));

p1.add(new JButton("OK"));

p1.add(new JButton("EXIT"));

 

// 创建面板,指定边框布局

JPanel p2 = new JPanel(new BorderLayout());

// 面板添加文本域,边框北部

p2.add(new JTextField("我在这里啊!!!"), BorderLayout.NORTH);

// 面板添加其他面板,边框中部.

p2.add(p1, BorderLayout.CENTER);

 

// 框架添加面板,框架的布局默认就是边框布局,面板指定位于框架西部

add(p2, BorderLayout.EAST);

// 框架添加按钮,位于框架总部.

add(new JButton("哈哈"), BorderLayout.CENTER);

 

}

 

public static void main(String[] args) {

TestPanel frame = new TestPanel();

frame.setTitle("JPanel");

frame.setSize(400, 260);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

该案例中创建了两个面板(Jpanel) , 面板的默认布局是流式布局.,可以使用setLayout 更改面板的布局( setLayout 是java.awt.Container定义的, Jpanel 是Container的子类).

该案例中

面板p1布局指定为GridLayout(网格布局)将12个按钮,添加进面板p1中.

面板p2 布局指定为BorderLayout,将p1面板添加进来, p1位于该面板的中部.再添加一个文本域,位于面板p2北部.

将面板p2添加进框架(框架布局默认为BorderLayout),位于框架的西部,在框架的中部添加一个按钮.

效果:

注意: 面板容器是一个轻量级的容器,该容器不能单独的使用,必须依赖与外层的顶层容器(JFrame)

Java事件监听机制

在上述的程序中,其中菜单条,菜单项,按钮等都是对象,当我们单击对象时,应该能够完成一些任务.例如在程序中通过鼠标操作时,单击,双击,鼠标移入,鼠标移出.能够执行一些任务,在Java中我们可以使用事件监听机制,在Java的事件监听机制中 ,当事件发生时(点击按钮,移动鼠标等,关闭窗口)会被一类对象发现并处理.

 

 

​​​​​​​事件和事件源

在运行java图形用户界面程序时,用户与程序交互,用户执行了某些操作时,会发生一些事情, 那么事件(event)可以定义为程序发生了某些事情的信号.典型用户操作就是用户移动鼠标,点击按钮,敲击键盘等.程序可以选择相应事件或者忽略事件。

能够创建一个事件并触发该事件的组件称为源对象。例如由于按钮能够点击, 那么按钮就是一个源对象,按钮被点击就是一个事件。

一个事件是事件类的实例对象。事件类的根类是java.util.EventObject。

事件对象包含事件相关的属性,可以使用EventObject类中的实例方法getSource获得事件的源对象。

EventObject类的子类可以描述特定类型的事件

例如:

用户动作

源对象

触发的事件类型

点击按钮

JButton

ActionEvent

文本域按回车

JTextField

ActionEvent

窗口打开,关闭,最小化,关闭

Window

WindowEvent

单击,双击,移动,鼠标

Component

MouseEvent

点击单选框

JradioButton

ItemEvent ActionEvent

点击复选框

JcheckBox

ItemEvent ActionEvent

 

​​​​​​​监听器

当源对象触发了一个事件,谁来处理这个事件?在Java中对此感兴趣的对象会处理它。对此感兴趣的对象称之为监听器(Listener)。

举例来说当我们点击一个按钮,想要按钮执行一些动作,需要一个对象来监控按钮,当点击按钮的事件发生时,该对象就会监听到按钮事件。进而可以执行一些动作。

例如:

 

Java中,对象表示的每个事件都是由java.util中EventObject类的子类,

例如: MouseEvent: 表示鼠标的动作,例如移动光标,单击,双击

KeyEvent: 表示键盘上的按键.

ActionEvent表示用户采取的用户界面操作,例如点击屏幕上的按钮.

​​​​​​​事件处理

Java对组件都有对应的事件监听器,和添加事件监听器方法。

例如: 按钮: 源对象,会触发

体验:当用户点击按钮时,按钮上的信息会发生变化

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

 

public class ShowButton extends JFrame {

public ShowButton() {

JButton button = new JButton("点我");

add(button);

 

// 添加鼠标监听事件

button.addActionListener(new ActionListener() {

 

@Override

public void actionPerformed(ActionEvent e) {

System.out.println("按钮被点击");

Object source = e.getSource();

JButton button = (JButton) source;

String text = button.getText();

if ("按钮被点击".equals(text)) {

button.setText("点我");

} else {

button.setText("按钮被点击");

}

}

});

}

 

public static void main(String[] args) {

ShowButton frame = new ShowButton();

frame.setTitle("我的框架");

frame.setSize(400, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

该案例中:构造方法创建了用户界面。只有一个JFrame框架和一个按钮。

按钮时事件的源。创建了一个监听器并注册到了按钮。通过匿名内部类的形式创建了监听器类

​​​​​​​窗口事件

Window类

java.awt

类 Window

java.lang.Object

      java.awt.Component

             java.awt.Container

                   java.awt.Window

Window 对象是一个顶层窗口。窗口(Window)对应的事件叫做窗口事件(WindowEvent),任何窗口(Window)以及窗口的子类都可能触发窗口事件: 打开窗口,正在关闭窗口,激活窗口,变成非活动窗口,最小化窗口和还原窗口。

Window添加窗口事件(WindowEvent)监听器的方法

void addWindowListener(WindowListener l)

          添加指定的窗口侦听器,以从此窗口接收窗口事件

 

监听Window窗口事件(WindowEvent)的监听器:WindowListener

java.awt.event

接口 WindowListener

用于接收窗口事件的侦听器接口。当通过打开、关闭、激活或停用、图标化或取消图标化而改变了窗口状态时,将调用该侦听器对象中的相关方法

 

WindowListener接口中定义的方法

抽象方法:

void windowActivated(WindowEvent e)

          激活窗口

 void windowClosed(WindowEvent e)

          关闭窗口

 void windowClosing(WindowEvent e)

          正在关闭窗口

 void windowDeactivated(WindowEvent e)

          变为非活动窗口

 void windowDeiconified(WindowEvent e)

          还原窗口    

 void windowIconified(WindowEvent e)

          最小化窗口

void windowOpened(WindowEvent e)

           打开窗口

  

 

 

 

 

案例: 框架的事件监听处理

javax.swing

类 JFrame

java.lang.Object

       java.awt.Component

              java.awt.Container

                    java.awt.Window

                          java.awt.Frame

                                javax.swing.JFrame

 

创建JFrame,Jfame是一个框架,属于窗体(Window)体系中的一员,也可以实现窗口的最大化,最小化,关闭,点击窗体,等一系列的操作。那么在用户触发这些事件发生时能够做一些工作,就需要注册事件监听器。

 

JFrame 是通过addWindowListener 方法就注册窗体事件监听器,该方法需要接受一个监听器(WindowListener)对象。查找API文档,发现WindowListener是一个接口,

我们需要窗口监听器(WindowListener)的实例对象,所以需要实现该接口,重写WindowListener接口的抽象方法。然后创建该实现类对象,作为参数传递给addWindowListener .

例如: 当像激活窗口这样的窗口事件发生时,windowActivated 方法就会触发。

代码如下:

import java.awt.event.WindowEvent;

import java.awt.event.WindowListener;

import javax.swing.JFrame;

 

public class WindowEventDemo extends JFrame {

 

WindowEventDemo() {

// this对象时JFrame的子类,而JFrame 类是Window体系中的一员所以具备添加窗口事件的方法

this.addWindowListener(new MyWindow());

}

 

public static void main(String[] args) {

WindowEventDemo frame = new WindowEventDemo();

frame.setTitle("我的框架");

frame.setSize(400, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

// 实现WindowListener

class MyWindow implements WindowListener {

// 激活窗口

public void windowActivated(WindowEvent e) {

System.out.println("激活窗口");

}

 

// 关闭窗口

public void windowClosed(WindowEvent e) {

System.out.println("关闭窗口");

}

 

// 正在关闭窗口

public void windowClosing(WindowEvent e) {

System.out.println("正在关闭窗口");

}

 

// 变为非活动窗口

public void windowDeactivated(WindowEvent e) {

System.out.println("变为非活动窗口");

}

 

// 还原窗口

public void windowDeiconified(WindowEvent e) {

System.out.println("还原窗口");

}

 

// 最小化窗口

public void windowIconified(WindowEvent e) {

System.out.println(" 最小化窗口");

}

 

// 打开窗口

public void windowOpened(WindowEvent e) {

System.out.println("打开窗口");

}

 

}

总结:Window类或者Window类的任何子类都可能会触发WindowEvent。因为JFrame是Window的子类。所以也可以触发WindowEvent。

​​​​​​​监听器接口适配器

因为WindowListener接口中的方法都是抽象的,所以即使程序中不想关注某些事件,但是还是要实现所以的方法,比较麻烦,为了方便起见,Java提供了一个针对WindowListener接口的实现类,该类中把WindowListener接口中的方法全部实现,只不过方法体都为空。

例如: 加入我们只对激活窗口感兴趣,那么只需要继承该实现类重写激活窗口的方法即可。简化了代码。

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import javax.swing.JFrame;

 

public class AdapterDemo extends JFrame {

AdapterDemo() {

addWindowListener(new MyAdapter());

}

 

public static void main(String[] args) {

AdapterDemo frame = new AdapterDemo();

frame.setTitle("我的框架");

frame.setSize(400, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

 

class MyAdapter extends WindowAdapter {

public void windowActivated(WindowEvent e) {

System.out.println("windowActivated....");

}

}

 

}

 

那么这个WindowAdapter类就叫做适配器类,是为了简化代码的书写而出现的。

适配器                            接口    

WindowAdapter               WindowListener

MouserAdapter  MouserListener

KeyAdapter           KeyListener

 

 

​​​​​​​鼠标键盘事件

当在一个组件上按下,释放,点击,移动,或者拖动鼠标时,就会产生鼠标事件。MouseEvent对象捕获这个事件。

MouseEvent

java.awt.event.MouseEvent

 

Java对鼠标事件提供了MouseListener 监听器接口,可以监听鼠标的按下,释放,输入,退出和点击动作。

MouseListener

用于接收组件上“感兴趣”的鼠标事件(按下、释放、单击、进入或离开)的侦听器接口

方法:

void mouseClicked(MouseEvent e)

          鼠标按键在组件上单击(按下并释放)时调用。

 void mouseEntered(MouseEvent e)

          鼠标进入到组件上时调用。

 void mouseExited(MouseEvent e)

          鼠标离开组件时调用。

 void mousePressed(MouseEvent e)

          鼠标按键在组件上按下时调用。

 void mouseReleased(MouseEvent e)

          鼠标按钮在组件上释放时调用。

 

 

 

按键事件可以利用键盘来控制和执行一些动作,如果按下、释放一个键就会触发按键事件。KeyEvent对象可以捕获按键的按下放开和敲击。KeyEvent提供了getkeyChar 来获取按键对应的字符。

java.awt.event.KeyEvent

 

char getKeyChar()

          返回与此事件中的键关联的字符。

 

Java提供了KeyListener监听器接口来监听按键事件。

 

KeyListener接口

用于接收键盘事件(击键)的侦听器接口。

void keyPressed(KeyEvent e)

          按下某个键时调用此方法。 

 void keyReleased(KeyEvent e)

          释放某个键时调用此方法。 

 void keyTyped(KeyEvent e)

          键入某个键时调用此方法。

 

 

案例:

演示鼠标事件和键盘事件,当在文本域中输入q 时程序会退出。

public class MouseEventDemo extends JFrame {

MouseEventDemo() {

JButton button = new JButton("ok");

JTextArea text = new JTextArea();

add(button, BorderLayout.NORTH);

add(text, BorderLayout.CENTER);

 

button.addMouseListener(new MouseListener() {

 

// 鼠标按钮在组件上释放时调用。

public void mouseReleased(MouseEvent e) {

System.out.println("鼠标释放");

 

}

 

// 鼠标按键在组件上按下时调用。

public void mousePressed(MouseEvent e) {

System.out.println("鼠标按下组件");

 

}

 

// 鼠标离开组件时调用。

public void mouseExited(MouseEvent e) {

System.out.println("鼠标离开组件");

 

}

 

// 鼠标进入到组件上时调用。

public void mouseEntered(MouseEvent e) {

// 鼠标进入

System.out.println("鼠标进入组件");

 

}

 

// 鼠标按键在组件上单击(按下并释放)时调用。

public void mouseClicked(MouseEvent e) {

System.out.println("鼠标单击组件");

 

}

});

text.addKeyListener(new KeyListener() {

 

// 键入某个键时调用此方法。

public void keyTyped(KeyEvent e) {

System.out.println("键入某个键");

if (e.getKeyChar() == 'q') {

System.exit(0);

}

}

// 释放某个键时调用此方法。

public void keyReleased(KeyEvent e) {

System.out.println("按键释放");

 

}

 

// 按下某个键时调用此方法。

public void keyPressed(KeyEvent e) {

System.out.println("键盘按下");

}

});

}

 

public static void main(String[] args) {

MouseEventDemo frame = new MouseEventDemo();

frame.setTitle("鼠标事件");

frame.setSize(400, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);

}

}

 

 

 

​​​​​​​
事件监听机制小结

一:确定事件源(容器或组件)

二:注册监听器

l  通过事件源对象的addXXXListener()方法将监听器对象注册到该事件源上。

三:监听器对象

l   注册监听器时,需要指定监听器对象。

以参数的形式进监听器对象传递给addXXXListener()

监听器对象是XXXListener的子类对象或者XXXAdapter的子类对象。

l 监听器对象一般用匿名内部类来表示。(简化书写)

l 在覆盖方法的时候,方法的参数一般是XXXEvent类型的变量接收。

l  事件触发后会把事件打包成对象传递给该变量。(其中包括事件源对象。通过getSource()或者getComponent()获取。)

四:常见的事件监听器

WindowListener        主要用于监听窗口

ActionListener          主要用于用监听组件对象的单击动作

MouseListener          鼠标监听器

KeyListener               监听键盘

…….

五:常见的事件适配器

WindowAdapter

MouseAdapter

KeyAdapter

夜光:作业

完成如下图像程序,在文本框中输入路径,显示该路径下的子文件和子目录。

设计简单的记事本程序。

 

 

 

====================================

 

  1. 批处理文件(bat)

简单的说,批处理的作用就是自动的连续执行多条命令 .编写bat处理文件可以使用记事本的方式:

常见批处理文件的命令:

echo 表示显示此命令后的字符 

tiltle 设置窗口的标题。

echo off 表示在此语句后所有运行的命令都不显示命令行本身 

color 设置窗体的字体颜色。

@与echo off相象,但它是加在每个命令行的最前面,表示运行时不显示这一行的命令行(只能影响当前行)。 

pause 运行此句会暂停批处理的执行并在屏幕上显示Press any key to continue...的提示,等待用户按任意键后继续 

rem 表示此命令后的字符为解释行(注释),不执行,只是给自己今后参考用的(相当于程序中的注释) 或者%注释的内容%

%[1-9]表示参数,参数是指在运行批处理文件时在文件名后加的以空格(或者Tab)分隔的字符串

    1. 单例

Singleton

是指只能拥有一个实例的类就是单例类。

私有构造方法。

获取方式

通过公共的静态方法创建单一的实例。

两种模式

懒汉模式 – 通常被称为延迟加载。注意存在线程安全问题.

饿汉模式

懒汉式的单例模式线程安全问题的解决方案:

class Single{

 

//声明本类的一个私有的成员变量

private static Single single;

 

//第一步 : 私有化构造方法

private Single(){

 

}

 //  第三步:提供一个公共的方法获取该类的实例对象

public static Single getInstance(){

if(single==null){

synchronized (single) {

if(single==null){

 single = new Single();

}

}

}

return single;

}

}

反射

类字节码文件是在硬盘上存储的,是一个个的.class文件。我们在new一个对象时,JVM会先把字节码文件的信息读出来放到内存中,第二次用时,就不用在加载了,而是直接使用之前缓存的这个字节码信息。

 

字节码的信息包括:类名、声明的方法、声明的字段等信息。在Java中“万物皆对象”,这些信息当然也需要封装一个对象,这就是Class类、Method类、Field类。

 

通过Class类、Method类、Field类等等类可以得到这个类型的一些信息,甚至可以不用new关键字就创建一个实例,可以执行一个对象中的方法,设置或获取字段的值,这就是反射技术

 

 

  1. Class类

    1. 获取Class对象的三种方式

 

Java中有一个Class类用于代表某一个类的字节码。

Java提供了三种方式获取类的字节码

forName()。forName方法用于加载某个类的字节码到内存中,并使用class对象进行封装

类名.class

对象.getClass()

 

/**

 * 加载类的字节码的3种方式

 * @throws Exception

 * */

public void test1() throws Exception {

// 方式一

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

// 方式二

Class clazz2 = Person.class;

// 方式三

Person p1 = new Person();

Class clazz3 = p1.getClass();

}

 

​​​​​​​观察者模式

有时又被称为

发布-订阅<Publish/Subscribe>模式、

模型-视图<Model/View>模式、

源-收听者<Source/Listener>模式

或从属者<Dependents>模式)

这是软件设计模式的一种。

观察者模式(Observer)完美的将观察者和被观察的对象分离开。

此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。

这通常透过呼叫各观察者所提供的方法来实现。

此种模式通常被用来实作事件处理系统。

有多个观察者时,不可以依赖特定的通知次序。

Swing大量使用观察者模式,许多GUI框架也是如此。

气象站:

public class WeatherStation {

 

private String weather;

 

String[] weathers = {"下雨","下雪","下冰雹","出太阳"};

 

static List<BookWeather> list = new ArrayList<BookWeather>();

 

Random random = new Random();

 

 

public void startWork(){

 

new Thread(){

 

@Override

public void run() {

while(true){

updateWeather();

try {

Thread.sleep(random.nextInt(1000)+500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}.start();

}

public void updateWeather(){

weather = weathers[random.nextInt(4)];

System.out.println("天气:"+ weather);

}

 

 

public String getWeather() {

return weather;

}

人:

public class Person implements BookWeather {

 

String name;

 

public  Person(String name){

this.name = name;

}

 

private WeatherStation station ;

 

public Person(String name,WeatherStation station){

this(name);

this.station = station;

}

 

//下雨","下雪 ","下冰雹","出太阳"

@Override

public void notifyWeather() {

String weather = station.getWeather();

if("下雨".equals(weather)){

System.out.println(name+"打着雨伞上班");

}else if("下雪".equals(weather)){

System.out.println(name+"溜冰 上班");

}else if("下冰雹".equals(weather)){

System.out.println(name+"带着头盔 上班");

}else if("出太阳".equals(weather)){

System.out.println(name+"嗮着太阳 上班");

}

}

 

}

 

测试类:

 

public class Test {

 

public static void main(String[] args) throws InterruptedException {

WeatherStation station = new WeatherStation();

station.startWork();

 

Person p1 = new Person("小明",station);

while(true){

p1.notifyWeather();

Thread.sleep(2000);

}

}

问题:天气变化两三次,小明才知道一次。

解决方案 :

package cn.itcast.test;

import java.util.List;

import java.util.ArrayList;

import java.util.Random;

public class WeatherStation {

 

private String weather;

 

String[] weathers = {"下雨","下雪","下冰雹","出太阳"};

 

private static List<BookWeather> list = new ArrayList<BookWeather>();

 

Random random = new Random();

 

public void addListaner(BookWeather e){

list.add(e);

}

 

public void startWork(){

 

new Thread(){

 

@Override

public void run() {

while(true){

updateWeather();

try {

Thread.sleep(random.nextInt(1000)+500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}.start();

}

public void updateWeather(){

weather = weathers[random.nextInt(4)];

System.out.println("天气:"+ weather);

for(BookWeather item : list){

item.notifyWeather(weather);

}

}

 

 

public String getWeather() {

return weather;

}

 

人:

public class Person implements BookWeather {

 

String name;

 

public  Person(String name){

this.name = name;

}

 

private WeatherStation station ;

 

public Person(String name,WeatherStation station){

this(name);

this.station = station;

}

 

//下雨","下雪 ","下冰雹","出太阳"

@Override

public void notifyWeather(String weather) {

if("下雨".equals(weather)){

System.out.println(name+"打着雨伞上班");

}else if("下雪".equals(weather)){

System.out.println(name+"溜冰 上班");

}else if("下冰雹".equals(weather)){

System.out.println(name+"带着头盔 上班");

}else if("出太阳".equals(weather)){

System.out.println(name+"嗮着太阳 上班");

}

}

 

}

接口:

 

public interface BookWeather {

 

public void notifyWeather(String weather);

}

 

 

public class Test {

 

public static void main(String[] args) throws InterruptedException {

WeatherStation station = new WeatherStation();

station.startWork();

 

Person p1 = new Person("小明");

Person p2 = new Person("小红");

Person p3 = new Person("小青 ");

station.addListaner(p1);

station.addListaner(p2);

station.addListaner(p3);

 

}

}

 

 

​​​​​​​通过Class类获取类型的一些信息

1. getName()类的名称(全名,全限定名)

2 getSimpleName()类的的简单名称(不带包名)

3. getModifiers(); 类的的修饰符

4.创建对象

无参数构造创建对象

newInstance()

5. 获取指定参数的构造器对象,并可以使用Constructor对象创建一个实例

Constructor<T> getConstructor(Class<?>... parameterTypes)

 

/**

 * 通过Class对象获取类的一些信息

 *

 * @throws Exception

 * */

private static void test2() throws Exception {

 

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

// 获取类的名称

String name = clazz1.getName();

System.out.println(name); // cn.itcast.gz.reflect.Person

// 获取类的简单名称

System.out.println(clazz1.getSimpleName()); // Person

// 获取类的修饰符

int modifiers = clazz1.getModifiers();

System.out.println(modifiers);

// 构建对象(默认调用无参数构造.)

Object ins = clazz1.newInstance();

Person p = (Person) ins;

System.out.println(p); // cn.itcast.gz.reflect.Person@c17164

// 获取指定参数的构造函数

Constructor<?> con = clazz1.getConstructor(String.class, int.class);

// 使用Constructor创建对象.

Object p1 = con.newInstance("jack", 28);

System.out.println(((Person) p1).getName());

}

 

​​​​​​​通过Class类获取类型中的方法的信息

1.获取公共方法包括继承的父类的方法

getMethods()返回一个数组,元素类型是Method

2.获取指定参数的公共方法

getMethod("setName", String.class);

3.获得所有的方法,包括私有

Method[] getDeclaredMethods()  

4.获得指定参数的方法,包括私有

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

/**

 * 获取公有方法.

 * @throws Exception

 * */

private static void test3() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

// 1.获取非私用方法(包括父类继承的方法)

Method[] methods = clazz1.getMethods();

System.out.println(methods.length);

for (Method m : methods) {

// System.out.println(m.getName());

if ("eat".equals(m.getName())) {

m.invoke(clazz1.newInstance(), null);

}

}

 

}

 

/**

 * 获取指定方法签名的方法

 *

 * @throws Exception

 * */

private static void test4() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

// 获取指定名称的函数

Method method1 = clazz1.getMethod("eat", null);

method1.invoke(new Person(), null);

}

 

/**

 * 获取指定方法名且有参数的方法

 *

 * @throws Exception

 * */

private static void test5() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

Method method = clazz1.getMethod("eat", String.class);

method.invoke(new Person(), "包子");

}

 

/**

 * 获取指定方法名,参数列表为空的方法.

 *

 * @throws Exception

 * */

private static void test4() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

// 获取指定名称的函数

Method method1 = clazz1.getMethod("eat", null);

method1.invoke(new Person(), null);

}

 

/**

 * 反射静态方法

 * @throws Exception

 * */

private static void test7() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

Method method = clazz1.getMethod("play", null);

method.invoke(null, null);

}

 

/**

 * 访问私有方法 暴力反射

 * @throws Exception

 * */

private static void test6() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

Method method = clazz1.getDeclaredMethod("movie", String.class);

method.setAccessible(true);

method.invoke(new Person(), "苍老师");

}

 

 

​​​​​​​通过Class类获取类型中的字段的信息

1.获取公共字段

Field[] getFields()  

2.获取指定参数的公共字段

Field getField(String name)  

3.获取所有的字段

Field[] getDeclaredFields()  

4.获取指定参数的字段,包括私用

Field getDeclaredField(String name)  

 

/**

 * 获取公有的字段

 * */

private static void test8() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

Field[] fields = clazz1.getFields();

Person p = new Person();

System.out.println(fields.length);

for (Field f : fields) {

System.out.println(f.getName());

if ("name".equals(f.getName())) {

System.out.println(f.getType().getName());

f.set(p, "jack");

}

}

System.out.println(p.getName());

 

}

 

 

/**

 * 获取私有的字段

 * @throws Exception

 * */

private static void test9() throws Exception {

Class clazz1 = Class.forName("cn.itcast.gz.reflect.Person");

Field field = clazz1.getDeclaredField("age");

System.out.println(field.getName());

field.setAccessible(true);

Person p = new Person();

field.set(p, 100);

System.out.println(p.getAge());

}

 

 

​​​​​​​工厂模式

Factory

例如:汽车销售商场

 

该模式将创建对象的过程放在了一个静态方法中来实现.在实际编程中,如果需要大量的创建对象,该模式是比较理想的.

public class Demo1 {

public static void main(String[] args) {

System.out.println("买宝马");

Car bmw = CarFactory("BMW");

bmw.run();

System.out.println("买大奔");

Car benz = CarFactory("Benz");

benz.run();

}

 

public static Car CarFactory(String carName) {

if ("BMW".equals(carName)) {

return new BMW();

} else if ("Benz".equals(carName)) {

return new Benz();

} else {

throw new RuntimeException("车型有误");

}

}

}

 

abstract class Car {

 

public abstract void run();

}

 

class BMW extends Car {

 

@Override

public void run() {

System.out.println("BMW跑跑");

}

}

 

class Benz extends Car {

 

@Override

public void run() {

System.out.println("Benz跑跑");

}

}

 

模拟spring工厂:

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.IOException;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

class Student{

 

private int id;

 

private  String name;

 

public Student(int id , String name){

this.id = id;

this.name = name;

}

 

public Student(){

 

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

 

@Override

public String toString() {

return this.id +"-"+this.name;

}

 

}

class Person{

 

private int age;

 

public Person(){

 

}

 

 

 

 

@Override

public String toString() {

return this.age+"";

}

}

public class Demo1 {

 

public static void main(String[] args) throws Exception {

Object o = getInstance();

System.out.println(o);

 

}

 

public static Object getInstance() throws Exception{

FileReader fileReader = new FileReader("src/info.txt");

BufferedReader bufferedReader = new BufferedReader(fileReader);

String line = bufferedReader.readLine();

Class clazz  = Class.forName(line);

Constructor c =  clazz.getConstructor(null);

Object c1 = c.newInstance(null);

while((line=bufferedReader.readLine())!=null){

String[] datas =  line.split("=");

Field f = clazz.getDeclaredField(datas[0]);

f.setAccessible(true);

if(f.getType()==int.class){

f.set(c1, Integer.parseInt(datas[1]));

}else{

//f.setAccessible(true);

f.set(c1,datas[1]);

}

}

return c1;

}

 

 

 

}

猜你喜欢

转载自blog.csdn.net/weixin_41987706/article/details/84833745