线程状态转换图(它有好几条路径)
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。【main】
public final ThreadGroup getThreadGroup()
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target, String name)
可能产生死锁的代码
package cn.itcast_02;public class MyLock { // 创建两把锁对象 public static final Object objA = new Object(); public static final Object objB = new Object();}ackage cn.itcast_02;public class DieLock extends Thread {private boolean flag;public DieLock(boolean flag) { this.flag = flag; }@Override public void run() { if (flag) { synchronized (MyLock.objA) { System.out.println("if objA"); synchronized (MyLock.objB) { System.out.println("if objB"); } } } else { synchronized (MyLock.objB) { System.out.println("else objB"); synchronized (MyLock.objA) { System.out.println("else objA"); } } } }}
package cn.itcast_02;/* * 同步的弊端: * A:效率低 * B:容易产生死锁 * 死锁: * 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。 * * 举例: * 中国人,美国人吃饭案例。 * 正常情况: * 中国人:筷子两支 * 美国人:刀和叉 * 现在: * 中国人:筷子1支,刀一把 * 美国人:筷子1支,叉一把 */public class DieLockDemo { public static void main(String[] args) { DieLock dl1 = new DieLock(true); DieLock dl2 = new DieLock(false);dl1.start(); dl2.start(); }} 等待唤醒机制代码 package cn.itcast_05;public class Student { String name; int age; boolean flag; // 默认情况是没有数据,如果是true,说明有数据}package cn.itcast_05;public class SetThread implements Runnable {private Student s; private int x = 0;public SetThread(Student s) { this.s = s; }@Override public void run() { while (true) { synchronized (s) { //判断有没有 if(s.flag){ try { s.wait(); //t1等着,释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } if (x % 2 == 0) { s.name = "林青霞"; s.age = 27; } else { s.name = "刘意"; s.age = 30; } x++; //x=1 //修改标记 s.flag = true; //唤醒线程 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。 } //t1有,或者t2有 } }}package cn.itcast_05;public class GetThread implements Runnable { private Student s;public GetThread(Student s) { this.s = s; }@Override public void run() { while (true) { synchronized (s) { if(!s.flag){ try { s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //林青霞---27 //刘意---30 //修改标记 s.flag = false; //唤醒线程 s.notify(); //唤醒t1 } } }}package cn.itcast_05;/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * 问题1:按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 * A:同一个数据出现多次 * B:姓名和年龄不匹配 * 原因: * A:同一个数据出现多次 * CPU的一点点时间片的执行权,就足够你执行很多次。 * B:姓名和年龄不匹配 * 线程运行的随机性 * 线程安全问题: * A:是否是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操作共享数据 是 * 解决方案: * 加锁。 * 注意: * A:不同种类的线程都要加锁。 * B:不同种类的线程加的锁必须是同一把。 * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 * 如何实现呢? * 通过Java提供的等待唤醒机制解决。 * 等待唤醒: * Object类中提供了三个方法: * wait():等待 * notify():唤醒单个线程 * notifyAll():唤醒所有线程 * 为什么这些方法不定义在Thread类中呢? * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 * 所以,这些方法必须定义在Object类中。 */public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s);//线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt);//启动线程 t1.start(); t2.start(); }} 等待唤醒机制代码金典版package cn.itcast_07;public class Student { private String name; private int age; private boolean flag; // 默认情况是没有数据,如果是true,说明有数据public synchronized void set(String name, int age) { // 如果有数据,就等待 if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 设置数据 this.name = name; this.age = age;// 修改标记 this.flag = true; this.notify(); }public synchronized void get() { // 如果没有数据,就等待 if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 获取数据System.out.println(this.name + "---" + this.age);// 修改标记 this.flag = false; this.notify(); }}package cn.itcast_07;public class SetThread implements Runnable {private Student s;private int x = 0;public SetThread(Student s) { this.s = s; }@Override public void run() { while (true) { if (x % 2 == 0) { s.set("林青霞", 27); } else { s.set("刘意", 30); } x++; } }}ackage cn.itcast_07;public class GetThread implements Runnable { private Student s;public GetThread(Student s) { this.s = s; }@Override public void run() { while (true) { s.get(); } }}package cn.itcast_07;/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo问题1:按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 * A:同一个数据出现多次 * B:姓名和年龄不匹配 * 原因: * A:同一个数据出现多次 * CPU的一点点时间片的执行权,就足够你执行很多次。 * B:姓名和年龄不匹配 * 线程运行的随机性 * 线程安全问题: * A:是否是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操作共享数据 是 * 解决方案: * 加锁。 * 注意: * A:不同种类的线程都要加锁。 * B:不同种类的线程加的锁必须是同一把。 * * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 * 如何实现呢? * 通过Java提供的等待唤醒机制解决。 * 等待唤醒: * Object类中提供了三个方法: * wait():等待 * notify():唤醒单个线程 * notifyAll():唤醒所有线程 * 为什么这些方法不定义在Thread类中呢? * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 * 所以,这些方法必须定义在Object类中。 * 最终版代码中: * 把Student的成员变量给私有的了。 * 把设置和获取的操作给封装成了功能,并加了同步。 * 设置或者获取的线程里面只需要调用方法即可。 */public class StudentDemo { public static void main(String[] args) { //创建资源 Student s = new Student(); //设置和获取的类 SetThread st = new SetThread(s); GetThread gt = new GetThread(s);//线程类 Thread t1 = new Thread(st); Thread t2 = new Thread(gt);//启动线程 t1.start(); t2.start(); }}
- 线程池
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool()public static ExecutorService newFixedThreadPool(int nThreads)public static ExecutorService newSingleThreadExecutor()
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
案例演示
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池
(shutdown()方法)
- 实现callable泛型接口,重写call()方法,也可以实现多线程,并且它有返回值(返回值类型跟方法的返回值类型一样),但是它依赖于线程池的存在,所以有一定的局限性。
实现Callable接口
步骤和刚才演示线程池执行Runnable对象的差不多。
但是还可以更好玩一些,求和案例演示
好处:
可以有返回值
可以抛出异常
弊端:
代码比较复杂,所以一般不用
多线程求和代码
package cn.itcast_10;import java.util.concurrent.Callable;/* * 线程求和案例 */public class MyCallable implements Callable{private int number;public MyCallable(int number) {this.number = number; }@Override public Integer call() throws Exception { int sum = 0; for (int x = 1; x <= number; x++) { sum += x; } return sum; }}package cn.itcast_10;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/* * 多线程实现的方式3: * A:创建一个线程池对象,控制要创建几个线程对象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:这种线程池的线程可以执行: * 可以执行Runnable对象或者Callable对象代表的线程 * 做一个类实现Runnable接口。 * C:调用如下方法即可 * Future submit(Runnable task) * Future submit(Callable task) * D:我就要结束,可以吗? * 可以。 */public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创建线程池对象 ExecutorService pool = Executors.newFixedThreadPool(2);// 可以执行Runnable对象或者Callable对象代表的线程 Future f1 = pool.submit(new MyCallable(100)); Future f2 = pool.submit(new MyCallable(200));// V get() Integer i1 = f1.get(); Integer i2 = f2.get();System.out.println(i1); System.out.println(i2);// 结束 pool.shutdown(); }} 定时器 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能Timer定时public Timer()public void schedule(TimerTask task, long delay)public void schedule(TimerTask task,long delay,long period)TimerTask任务【该类是一个抽象类】public abstract void run()public boolean cancel()开发中Quartz是一个完全由java编写的开源调度框架。 匿名内部类使用多线程匿名内部类方式使用多线程new Thread(){代码…}.start();New Thread(new Runnable(){代码…}).start();package cn.itcast_11;/* * 匿名内部类的格式: * new 类名或者接口名() { * 重写方法; * }; * 本质:是该类或者接口的子类对象。 */public class ThreadDemo { public static void main(String[] args) { // 继承Thread类来实现多线程 new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":"+ x);}} }.start();// 实现Runnable接口来实现多线程 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":"+ x); } } }) { }.start();// 更有难度的(两个同时有的时候调用的是子类对象的,并不是Runnable的) new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println("hello" + ":" + x); } } }) { public void run() { for (int x = 0; x < 100; x++) { System.out.println("world" + ":" + x); } } }.start(); }}
- 经典面试题(线程部分)
多线程有几种实现方案,分别是哪几种?
两种。
继承Thread类
实现Runnable接口
扩展一种:实现Callable接口。这个得和线程池结合。
2:同步有几种方式,分别是什么?
两种。
同步代码块
同步方法
3:启动一个线程是run()还是start()?它们的区别?
start();
run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用
start():启动线程,并由JVM自动调用run()方法
4:sleep()和wait()方法的区别
sleep():必须指时间;不释放锁。
wait():可以不指定时间,也可以指定时间;等待的时候释放锁。
5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
而Object代表任意的对象,所以,定义在这里面。
6:线程的生命周期图
新建 -- 就绪 -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡
建议:画图解释。
- 定时器经典代码
package cn.itcast_12;import java.io.File;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;/* * 需求:在指定的时间删除我们的指定目录(我使用项目路径下的demo) */class DeleteFolder extends TimerTask {@Override public void run() { File srcFolder = new File("demo"); deleteFolder(srcFolder); }// 递归删除目录 public void deleteFolder(File srcFolder) { File[] fileArray = srcFolder.listFiles(); if (fileArray != null) { for (File file : fileArray) { if (file.isDirectory()) { deleteFolder(file); } else { System.out.println(file.getName() + ":" + file.delete()); } } System.out.println(srcFolder.getName() + ":" + srcFolder.delete()); } }}public class TimerTest { public static void main(String[] args) throws ParseException { Timer t = new Timer();String s = "2014-11-27 15:45:00"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(s);t.schedule(new DeleteFolder(), d); }} 定时器经典代码2package cn.itcast_12;import java.util.Timer;import java.util.TimerTask; /* * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 * 依赖Timer和TimerTask这两个类: * Timer:定时 * public Timer() * public void schedule(TimerTask task,long delay) * public void schedule(TimerTask task,long delay,long period) * public void cancel() * TimerTask:任务 */public class TimerDemo { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); // 3秒后执行爆炸任务 // t.schedule(new MyTask(), 3000); //结束任务 t.schedule(new MyTask(t), 3000); }} // 做一个任务class MyTask extends TimerTask {private Timer t;public MyTask(){}public MyTask(Timer t){ this.t = t; }@Override public void run() { System.out.println("beng,爆炸了"); t.cancel(); }}
- 设计模式不是一种方法或技术,而是一种思想。
- 设计模式的分类
设计模式的分类
创建型模式 对象的创建
结构型模式 对象的组成(结构)
行为型模式 对象的行为
创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)
结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)
行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)
简单工厂模式概述
又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
优点
客户端不需要在负责对象的创建,从而明确了各个类的职责
缺点
这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
动物抽象类:public abstract Animal { public abstract void eat(); }
具体狗类:public class Dog extends Animal {}
具体猫类:public class Cat extends Animal {}
开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。
public class AnimalFactory { private AnimalFactory(){} //public static Dog createDog() {return new Dog();} //public static Cat createCat() {return new Cat();} //改进 public static Animal createAnimal(String animalName) { if(“dog”.equals(animalName)) {} else if(“cat”.equals(animaleName)) { }else { return null; } }}
- 工厂方法模式
工厂方法模式概述
工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
优点
客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
缺点
需要额外的编写代码,增加了工作量
动物抽象类:public abstract Animal { public abstract void eat(); }
工厂接口:public interface Factory {public abstract Animal createAnimal();}
具体狗类:public class Dog extends Animal {}
具体猫类:public class Cat extends Animal {}
开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。发现每次修改代码太麻烦,用工厂方法改进,针对每一个具体的实现提供一个具体工厂。
狗工厂:public class DogFactory implements Factory {
public Animal createAnimal() {…}
}
猫工厂:public class CatFactory implements Factory {
public Animal createAnimal() {…}
}
- 单例模式:保证类在内存中只有一个对象 (饿汉式:类已加载就创建对象,懒汉式:用得时候才去创建对象。
单例设计模式概述
单例模式就是要确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。
优点
在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
缺点
没有抽象层,因此扩展很难。
职责过重,在一定程序上违背了单一职责
- Runtime类就是按照单例设计模式的饿汉式设计的(它是一个可以操作系统命令(dos命令)的类)
- 单例设计模式饿汉式
package cn.itcast_03;
public class Student {
// 构造私有
private Student() {
}
// 自己造一个
// 静态方法只能访问静态成员变量,加静态
// 为了不让外界直接访问修改这个值,加private
private static Student s = new Student();
// 提供公共的访问方式
// 为了保证外界能够直接使用该方法,加静态
public static Student getStudent() {
return s;
}
}
package cn.itcast_03;
/*
* 单例模式:保证类在内存中只有一个对象。
*
* 如何保证类在内存中只有一个对象呢?
* A:把构造方法私有
* B:在成员位置自己创建一个对象
* C:通过一个公共的方法提供访问
*/
public class StudentDemo { public static void main(String[] args) { // Student s1 = new Student(); // Student s2 = new Student(); // System.out.println(s1 == s2); // false // 通过单例如何得到对象呢? // Student.s = null; Student s1 = Student.getStudent(); Student s2 = Student.getStudent(); System.out.println(s1 == s2); System.out.println(s1); // null,cn.itcast_03.Student@175078b System.out.println(s2);// null,cn.itcast_03.Student@175078b }} 单例设计模式懒汉式package cn.itcast_03; /* * 单例模式: * 饿汉式:类一加载就创建对象 * 懒汉式:用的时候,才去创建对象*/public class Teacher { private Teacher() { } private static Teacher t = null; public synchronized static Teacher getTeacher() { // t1,t2,t3 if (t == null) { //t1,t2,t3 t = new Teacher(); } return t; }}package cn.itcast_03; public class TeacherDemo { public static void main(String[] args) { Teacher t1 = Teacher.getTeacher(); Teacher t2 = Teacher.getTeacher(); System.out.println(t1 == t2); System.out.println(t1); // cn.itcast_03.Teacher@175078b System.out.println(t2);// cn.itcast_03.Teacher@175078b }}
- 面试题关于单例设计模式
面试题:单例模式的思想是什么?请写一个代码体现。
*
* 开发:饿汉式(是不会出问题的单例模式)
* 面试:懒汉式(可能会出问题的单例模式)
* A:懒加载(延迟加载)
* B:线程安全问题
* a:是否多线程环境(可能没有) 是
* b:是否有共享数据 是
* c:是否有多条语句操作共享数据 是
- Java为GUI提供的对象都存在java.Awt和javax.Swing两个包中。
- java.awt:Abstract Window ToolKit (抽象窗口工具包),需要调用本地系统方法实现功能。属重量级控件。
24.javax.swing:在AWT的基础上,建立的一套图形界面系统,其中提供了更多的组件,而且完全由Java实现。增强了移植性,属轻量级控件。(这里的轻重是指依赖当前系统的程度,越重依赖性越高,这样就不方便移植)
- 图形界面的单位默认是px,(比如1024*12,就是将一张纸分成1024*12份,每一份的大小就是一像素)窗体的默认位置是左上角。
- 尽量将窗体的显示放到后面,将什么都设置好后再让其显示,这样最好,加入先让其显示在设置属性,会有一个变化的过程,不符合实际需求。
- 父类实现了一个接口,那么子类就不需要在重写该接口中的方法了,因为子类已经间接的重写了(适配器模式)
- 窗体的设置和关闭(关闭第二种用得是适配器的原理)
package cn.itcast_02;import java.awt.Frame;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.awt.event.WindowListener;public class FrameDemo { public static void main(String[] args) { // 创建窗体对象 Frame f = new Frame("窗体关闭案例");// 设置窗体属性 f.setBounds(400, 200, 400, 300);// 让窗体关闭 //事件源 //事件:对窗体的处理 //事件处理:关闭窗口(System.exit(0)); //事件监听// f.addWindowListener(new WindowListener() {// @Override// public void windowOpened(WindowEvent e) {// }// @Override// public void windowIconified(WindowEvent e) {// }// @Override// public void windowDeiconified(WindowEvent e) {// }// @Override// public void windowDeactivated(WindowEvent e) {// }// @Override// public void windowClosing(WindowEvent e) {// System.exit(0);// }// @Override// public void windowClosed(WindowEvent e) {// }// @Override// public void windowActivated(WindowEvent e) {// }// }); //用适配器类改进 f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); // 设置窗体可见 f.setVisible(true); }}
- 匿名(局部)内部类访问局部变量,该局部变量要设置成final.
- 数据转移
package cn.itcast_05;import java.awt.Button;import java.awt.FlowLayout;import java.awt.Frame;import java.awt.TextArea;import java.awt.TextField;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;public class FrameDemo { public static void main(String[] args) { // 创建窗体对象 Frame f = new Frame("数据转移"); // 设置窗体属性和布局 f.setBounds(400, 200, 400, 300); f.setLayout(new FlowLayout());// 创建文本框 final TextField tf = new TextField(20); // 创建按钮 Button bu = new Button("数据转移"); // 创建文本域 final TextArea ta = new TextArea(10, 40);// 把组件添加到窗体 f.add(tf); f.add(bu); f.add(ta);// 设置窗体关闭 f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } });// 对按钮添加事件 bu.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 获取文本框的值 String tf_str = tf.getText().trim(); // 清空数据 tf.setText("");// 设置给文本域 // ta.setText(tf_str); // 追加和换行 ta.append(tf_str + "\r\n"); //获取光标 tf.requestFocus(); } });// 设置窗体显示 f.setVisible(true); }}
- 改变颜色
package cn.itcast_06;import java.awt.Button;import java.awt.Color;import java.awt.FlowLayout;import java.awt.Frame;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;public class FrameDemo { public static void main(String[] args) { // 创建窗体对象 final Frame f = new Frame("更改背景色"); // 设置窗体属性和布局 f.setBounds(400, 200, 400, 300); f.setLayout(new FlowLayout());// 创建四个按钮 Button redButton = new Button("红色"); Button greenButton = new Button("绿色"); Button buleButton = new Button("蓝色");// 添加按钮 f.add(redButton); f.add(greenButton); f.add(buleButton);// 设置窗体关闭 f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } });// 对按钮添加动作事件 // redButton.addActionListener(new ActionListener() { // @Override // public void actionPerformed(ActionEvent e) { // f.setBackground(Color.RED); // } // });// 对按钮添加鼠标点击事件 // redButton.addMouseListener(new MouseAdapter() { // @Override // public void mouseClicked(MouseEvent e) { // f.setBackground(Color.RED); // } // }); // 对按钮添加鼠标的进入事件 redButton.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { f.setBackground(Color.RED); } });redButton.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { f.setBackground(Color.WHITE); } });greenButton.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { f.setBackground(Color.GREEN); } });greenButton.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { f.setBackground(Color.WHITE); } });buleButton.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { f.setBackground(Color.BLUE); } });buleButton.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { f.setBackground(Color.WHITE); } }); // 设置窗体显示 f.setVisible(true); }}
- 只能输入数字
package cn.itcast_07;import java.awt.FlowLayout;import java.awt.Frame;import java.awt.Label;import java.awt.TextField;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;/* * 你输入的如果是非数字字符,就取消你键盘录入的效果。 */public class FrameDemo { public static void main(String[] args) { // 创建窗体对象并设置属性 Frame f = new Frame("不能输入非数字字符"); f.setBounds(400, 200, 400, 300); f.setLayout(new FlowLayout());// 创建Label标签对象 Label label = new Label("请输入你的QQ号码,不能是非数字,不信你试试"); TextField tf = new TextField(40);// 添加到窗体上 f.add(label); f.add(tf);// 设置窗体关闭 f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } });// 给文本框添加事件 tf.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { // 如果你取得的字符不是数字字符就取消事件 // 思路:先获取字符,判断字符,取消事件 // char getKeyChar() char ch = e.getKeyChar();// System.out.println(ch); if(!(ch>='0' && ch<='9')){ e.consume(); } } });// 设置窗体可见 f.setVisible(true); }} 多级菜单 package cn.itcast_09;import java.awt.FlowLayout;import java.awt.Frame;import java.awt.Menu;import java.awt.MenuBar;import java.awt.MenuItem;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.io.IOException; /* * 多级菜单 */public class FrameDemo { public static void main(String[] args) { // 创建窗体对象并设置属性 final Frame f = new Frame("多级菜单"); f.setBounds(400, 200, 400, 300); f.setLayout(new FlowLayout()); final String name = f.getTitle();// 创建菜单栏 MenuBar mb = new MenuBar(); // 创建菜单 Menu m1 = new Menu("文件"); Menu m2 = new Menu("更改名称"); // 创建菜单项 final MenuItem mi1 = new MenuItem("好好学习"); final MenuItem mi2 = new MenuItem("天天向上"); MenuItem mi3 = new MenuItem("恢复标题"); MenuItem mi4 = new MenuItem("打开记事本"); MenuItem mi5 = new MenuItem("退出系统");// 谁添加谁呢 m2.add(mi1); m2.add(mi2); m2.add(mi3); m1.add(m2); m1.add(mi4); m1.add(mi5); mb.add(m1);// 设置菜单栏 f.setMenuBar(mb);// 设置窗体关闭 f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { f.setTitle(mi1.getLabel()); } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { f.setTitle(mi2.getLabel()); } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { f.setTitle(name); } }); mi4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Runtime r = Runtime.getRuntime(); try { r.exec("notepad"); } catch (IOException e1) { e1.printStackTrace(); } } });mi5.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } });// 设置窗体可见 f.setVisible(true); }}
如何让Netbeans的东西Eclipse能访问。
在Eclipse中创建项目,把Netbeans项目的src下的东西给拿过来即可。
注意:修改项目编码为UTF-8
- 菜单的使用顺序
MenuBar,Menu,MenuItem
先创建菜单条,再创建菜单,每一个菜单中建立菜单项。
也可以菜单添加到菜单中,作为子菜单。
通过setMenuBar()方法,将菜单添加到Frame中。
- 端口可以区分 不同的应用程序。网络编程三要素:ip地址:这是要找到目的地。端口:这是找到目的地之后,找到不同的应用程序。协议:双方通信的规则。
- 网络地址的分类以及一些私有地址
A:所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,比特换算成字节,就是4个字节。例如一个采用二进制形式的IP地址是“00001010000000000000000000000001”,这么长的地址,人们处理起来也太费劲了。为了方便人们的使用,IP地址经常被写成十进制的形式,中间使用符号“.”分开不同的字节。于是,上面的IP地址可以表示为“10.0.0.1”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多。
B:IP地址的组成
IP地址 = 网络号码+主机地址
A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码
特殊地址:
127.0.0.1 回环地址,可用于测试本机的网络是否有问题. ping 127.0.0.1
DOS命令 ipconfig:查看本机IP地址
xxx.xxx.xxx.0 网络地址
xxx.xxx.xxx.255 广播地址
A类 1.0.0.1---127.255.255.254
(1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)
(2)127.X.X.X是保留地址,用做循环测试用的。
B类 128.0.0.1---191.255.255.254
172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C类 192.0.0.1---223.255.255.254
192.168.X.X是私有地址
D类 224.0.0.1---239.255.255.254
E类 240.0.0.1---247.255.255.254
为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用。
- 两种协议的优缺点
UDP
将数据源和目的封装成数据包中,不需要建立连接;每个数据报的大小在限制在64k;因无连接,是不可靠协议;不需要建立连接,速度快
TCP
建立连接,形成传输数据的通道;在连接中进行大数据量传输;通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低
总结:
udp:
面向无连接。不可靠。速度快。将数据封包传输,数据包最大64k。
举例:
聊天留言,在线视频,视频会议,发短信,邮局包裹。
tcp:面向连接。安全可靠效率稍低。通过三次握手确保连接的建立。
举例:
下载,打电话,QQ聊天(你在线吗,在线,就回应下,就开始聊天了)
- Socket编程其实就是网络编程
Socket套接字:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket原理机制:
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
- 发送和接收数据(在实际开发中,应该是先写接收端,在写发送端,因为没有接收端,发送端就没有意义)
package cn.itcast_02;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;/* * UDP协议发送数据: * A:创建发送端Socket对象 * B:创建数据,并把数据打包 * C:调用Socket对象的发送方法发送数据包 * D:释放资源 */public class SendDemo { public static void main(String[] args) throws IOException { // 创建发送端Socket对象 // DatagramSocket() DatagramSocket ds = new DatagramSocket();// 创建数据,并把数据打包 // DatagramPacket(byte[] buf, int length, InetAddress address, int port) // 创建数据 byte[] bys = "hello,udp,我来了".getBytes(); // 长度 int length = bys.length; // IP地址对象 InetAddress address = InetAddress.getByName("192.168.12.92"); // 端口 int port = 10086; DatagramPacket dp = new DatagramPacket(bys, length, address, port);// 调用Socket对象的发送方法发送数据包 // public void send(DatagramPacket p) ds.send(dp);// 释放资源 ds.close(); }}package cn.itcast_02;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;/* * UDP协议接收数据: * A:创建接收端Socket对象 * B:创建一个数据包(接收容器) * C:调用Socket对象的接收方法接收数据 * D:解析数据包,并显示在控制台 * E:释放资源 */public class ReceiveDemo { public static void main(String[] args) throws IOException { // 创建接收端Socket对象 // DatagramSocket(int port) DatagramSocket ds = new DatagramSocket(10086);// 创建一个数据包(接收容器) // DatagramPacket(byte[] buf, int length) byte[] bys = new byte[1024]; int length = bys.length; DatagramPacket dp = new DatagramPacket(bys, length);// 调用Socket对象的接收方法接收数据 // public void receive(DatagramPacket p) ds.receive(dp); // 阻塞式// 解析数据包,并显示在控制台 // 获取对方的ip // public InetAddress getAddress() InetAddress address = dp.getAddress(); String ip = address.getHostAddress(); // public byte[] getData():获取数据缓冲区 // public int getLength():获取数据的实际长度 byte[] bys2 = dp.getData(); int len = dp.getLength(); String s = new String(bys2, 0, len); System.out.println(ip + "传递的数据是:" + s);// 释放资源 ds.close(); }}
--------》待续