枚举
作用:限制某个类创建对象的个数。
枚举也是一个特殊的类,编译后也会生成一个class文件,他的构造方法都是private的。
想要创建某个类必须要通过他的构造方法,在jdk5.0之前,我们要想限制某个类创建对象的个数,我们可以把这个类的构造方法私有化,这样别的类就不能通过构造方法创建这个类的对象了,但是我们还想要这个类有对象给其他类使用,这个时候我们可以在这个类内创建自己的对象,再把它定义成静态的,这样就可以通过类名.对象名直接访问了,我们也不想对象的个数不会因为调用它的类给他赋值为null而导致此对象引用为空变成垃圾被回收。
class Grade{ //A B C D E 表示成绩
private Grade(){//不想要外界创建这个类的对象,我们可以把这个类的构造方法定义成private的
}
/*在本类中创建自己的对象,定义成public static的,这样别的类要向使用这个类的对象就可以通过类名.对象名直接调用,同时我们
不想要外界随意修改这个对象,可以把这个对象声明成final的*/
public static final Grade A=new Grade();
public static final Grade B=new Grade();
public static final Grade C=new Grade();
public static final Grade D=new Grade();
public static final Grade E=new Grade();
public void test(){
System.out.println("test");
}
private void test2(){
System.out.println("这个方法不能被调用");
}
}
public class Demo8{
public static void main(String[] args) {
Grade a=Grade.A;//通过类名.对象名直接调用这个类的对象
a.test();//可以直接调用该类中的公共方法
// a.test2();//私有化不能调用
}
}
可以写成枚举类:
enum Grade2{ //结果:A,10
A,B,C,D,E; //;可省略
int x=10;//成员变量要定义在枚举的下面
public void test(){
System.out.println("test");
}
}
public class Demo2 {
public static void main(String[] args) {
//相当于前面说的静态final,可以直接类名.对象名调用,由于枚举类中已经重写了toString方法,所以会直接显示对象名
System.out.println(Grade2.A);
System.out.println(Grade2.A.x);
}
}
枚举的应用
enum Gender{ //定义了性别类,这个类只有两个对象,限定了界限
MAN,WOMEN
}
class Person{
private String name;
private int age;
Gender sex; //sex的类型为枚举类型,同包下只要定义了枚举就可以直接使用,枚举限定了值的选择,解决了随便赋值的问题
public Person(String name, int age, Gender sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return name+"..."+age+"..."+sex;
}
}
public class Demo3 { //zhangsan...20...MAN
public static void main(String[] args) {
Person p=new Person("zhangsan", 20, Gender.MAN);
System.out.println(p);
}
}
枚举可以用在switch语句中,switch支持byte,int,short,char,string,enum 类型的变量
//枚举可以用在switch语句中
enum Grade3{
A,B,C,D;
}
public class Demo4 {
public static void main(String[] args) {
switch (Grade3.A) {
case A:
System.out.println("A");
break;
case B:
System.out.println("B");
break;
case C:
System.out.println("C");
break;
case D:
System.out.println("D");
break;
default:
break;
}
}
}
枚举类中的成员
变量,静态变量,方法,静态方法,构造方法(private),抽象方法(必须要实现)。构造方法必须是私有的,也可以有重载形式,调用重载的构造方法,枚举类的变量也要加上实参来和重载的构造方法匹配。
枚举类的构造方法也有重载形式,重载的构造方法也必须是private的,只要在枚举后面加个括号,里面写上构造方法对应的实参即可。默认构造参数如果有输出语句的话,即使main方法不调用别的对象,这个构造函数的输出语句也会默认打印,因为所有的对象都已经创建出来了。
enum Week {
MON(5) {
@Override
void info() {
System.out.println("星期一");
}
},
TUE {
@Override
void info() {
System.out.println("星期二");
}
};
private Week() {//默认无参构造方法,enum的构造方法必须是private的
System.out.println("测试,每有一个对象都会调用此构造方法,即使在main方法中没有调用别的对象也会输出这句话");
}
private Week(int num){//有参构造方法
System.out.println(num);
}
// The enum constants must implement the abstract method
// info(),有抽象方法必须要实现,可以使用匿名内部类的方式
abstract void info();
}
public class Demo3 {
public static void main(String[] args) {
/*只要调用了一个枚举对象,构造方法就会输出打印所有的枚举对象,如果他的构造方法中有输出语句即使你没有输出这个对象,
构造方法中的输出语句也会默认输出到控制台,所以此处会先输出有参构造函数中的5,然后再输出无参构造函数中的那句话,且只会输出一次*/
Week w = Week.MON;
Week w2=Week.TUE;
Week[] values = Week.values();
for(Week w3:values) {
System.out.println(w3+"___");
}
System.out.println("==="+w.name());
System.out.println(w2.compareTo(w));
w.info();// 调用他本身已经重写的info()方法,
w2.info();
System.out.println("w.ordinal()--"+w.ordinal());// 枚举对象的序号,默认从0开始
System.out.println("w2.ordinal()--"+w2.ordinal());
}
}
常用方法
枚举.values(): 获取所有枚举值名,返回的是枚举值名的数组,遍历后即可得到所有的枚举值
name(): 获取指定的枚举名相当于toString方法
compareTo(): 比较两个枚举的顺序,返回一个int值,是这两个枚举值之间序号的差值,同一个枚举值返回的是0
ordinal():返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
线程生命周期
生命周期指的是线程从无到创建再到无的过程。
线程可以总结有以下状态:
新建状态:new Thread()或者是new Thread(Runnable)
就绪状态:start()方法,有运行资格,等待CPU调度
运行状态:执行run()
消亡状态:run()执行完了
阻塞状态:运行过程中,sleep(millis),wait()-notify(),I/O,这时候主动放弃执行资格,CPU不再调度,阻塞解除后就会回到就绪状态等待再次调度,而不会直接回到运行状态。
注意:上述状态只是理论上的,java中有一个类专门描述线程状态的---Thread.State
参考文章:https://blog.csdn.net/pange1991/article/details/53860651
IO时的线程状态以及CPU状态:https://my.oschina.net/goldenshaw/blog/705397
NEW (新建状态)至今尚未启动的线程的状态。 |
RUNNABLE (就绪状态+运行状态)//启动start()之后就会出现这种状态可运行线程的线程状态。 |
BLOCKED (阻塞状态)//带同步锁的时候会出现这种状态受阻塞并且正在等待监视器锁的某一线程的线程状态。 |
|
WAITING (阻塞状态)//执行了wait()方法会出现某一等待线程的线程状态。 |
TERMINATED (消亡状态)已终止线程的线程状态。 |
下面要运用Thread类中的getState()方法验证以上状态
Thread.State | getState() 返回该线程的状态。 |
创建线程的四种方式
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
4)使用线程池例如用Executor框架
具体参考如下链接:https://blog.csdn.net/m0_37840000/article/details/79756932
class myThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new myThread());
System.out.println(t.getState());//线程还没开始,现在应该是新建状态,返回new
t.start();
System.out.println(t.getState());//开启线程,现在应该是就绪状态+运行状态,返回RUNNABLE
Thread.sleep(1000);
System.out.println(t.getState());
}
}
单例设计模式
设计模式:通用问题的通用解决方案,共23种,将这些模式分为:创建型,行为型,结构型。
单例模式:
作用:保证内存中对象的唯一性。
步骤:
- 私有化构造方法
- 在本类中创建该类的实例对象
- 在类中创建一个公共的方法,用于返回该类实例对象。
饿汉式+懒汉式:这单例设计模式效果和枚举以及私有化构造方法然后定义final本类对象效果一样。
懒汉式:类一加载对象就创建好了,如果不需要对象,可能会造成空间浪费;
饿汉式:什么时候使用才创建对象---对象的延时加载。弊端:多线程访问时可能出现线程不安全,对象不唯一。解决办法是加一个同步锁,锁对象用类名.class即可,因为方法是静态的。
//单例模式-饿汉式
class Single1 {// 类一加载对象就创造好了,可能会造成空间浪费,线程安全
private Single1() {}// 1.私有化构造方法
private static final Single1 s = new Single1();// 2.在本类中创建该类实例对象
public static Single1 getInstance() {// 3.在类中创建一个方法,用于返回该类实例对象
return s;
}
}
// 单例模式-懒汉式
class Single2 {// 什么时候用什么时候创建对象,不会造成空间浪费---对象的延时加载
// 由于该类对象不是一步创建完成的,多线程访问时可能会出现对象不唯一,不安全,解决办法:加一个同步锁
private Single2() {}// 1.私有化构造方法
private static Single2 s = null;// 2.在本类中创建自己的对象,注意这里千万不能用final修饰,否则下面就不能对s赋值了
public static Single2 getInstance() {// 3.创建一个公共方法,用于返回该类实例对象
if (s == null)//不等于null时直接拿来使用就可以,不用再判断同步锁
synchronized (Single2.class) {// 加一个同步锁,防止线程不安全
/*不加的话第一次s获得了一个对象new Single2,第二次进来调用方法,此时s已经有一个new Single2的对象了,但是没有判断是否为空,
于是他又创建了一个new Single2的对象,每new一次都是一个新的对象,地址值不同,所以两次不相等*/
if(s==null)
s = new Single2();
}
return s;
}
}
public class Demo7 {
public static void main(String[] args) {
Single1 s1 = Single1.getInstance();
Single1 s2 = Single1.getInstance();
System.out.println(s1 == s2);// true,证明对象唯一
Single2 s3 = Single2.getInstance();
Single2 s4 = Single2.getInstance();
System.out.println(s3 == s4);// true,证明对象唯一
}
}
懒汉式三种线程安全的写法(双重检测可以用volatile关键字修饰省略一层检测)
1、在getInstance方法上加同步
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
2.1、双重检查锁定
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
2.2、volatile关键字
public class Singleton {
//这样的写法是能避免无序写入的问题。因为别的线程进入不了方法体,除非当前线程释放锁。这样就能确保实例化完成。
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
3、静态内部类
public class Singleton {
// 静态内部类初始化即加载,且仅加载一次,保证了对象的唯一性
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
总结:
静态内部类这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
优点:
(1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
(2)由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,比如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
(3)单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点:
(1)单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
(2)单例对象如果持有Context,那么很容易引发内存泄露,此时需要注意传给单例对象的Context最好是Application Context。
线程池
由于创建和销毁线程耗费时间比任务时间长,因此,为了解决这个问题,出现了线程池的思想。即将创建好的线程对象存放到线程池中,这些线程对象,等待接受任务,接收到任务就去执行,任务结束就回到线程池。如果线程池中没有可用的线程,则该任务就会等待,直到有空闲线程来接收任务。
创建线程池对象:java.util.concurrent.Excutors
newFixedThreadPool(int nThreads):创建固定个数的线程对象的线程池。返回ExcutorService对象
newSingleThreadExcutorService(): 创建单个线程对象的线程池。返回ExcutorService对象
static ExecutorService | newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个可重复使用的固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。
|
ExcutorService接口中的方法:
Future<?> | submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
void | shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 |
List<Runnable> | shutdownNow() 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 |
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyRun2 implements Runnable{
public void run(){
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...."+i);
}
}
}
class MyRun3 implements Runnable{
public void run(){
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--------"+i);
}
}
}
class MyRun4 implements Runnable{
public void run(){
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"==============="+i);
}
}
}
public class Demo8 {
public static void main(String[] args) {
//创建线程池对象,有两个线程对象
ExecutorService es = Executors.newFixedThreadPool(2);
//将任务进行提交,2,3先交替运行,等到有一个线程结束后再接手下一个任务
es.submit(new MyRun2());
es.submit(new MyRun3());
es.submit(new MyRun4());
}
}