1.概述:所谓的包装类就是基本类型对应的类(引用类型),我们需要将基本类型转成包装类,从而让基本类型具有类的特性(说白了,就是将基本类型的数据转成包装类,就可以使用包装类中的方法来操作此数据)
2.为啥要学包装类:
a.将来有一些特定操作,调用方法需要传递包装类
比如:ArrayList集合,add(Object obj),我们只能传递Object的子类对象,此时如果我们想传递一些基本类型的数据,Object作为一个引用类型不能直接接受基本类型,所以我们需要先将基本类型转成包装类传递到Object中
3.将来我们需要让基本类型和包装类互相转换,为啥:
a.基本类型为啥转成包装类:
调用某个方法,方法参数传递Object类型,ArrayList集合,add(Object obj),我们只能传递Object的子类对象,此时如果我们想传递一些基本类型的数据,Object作为一个引用类型不能直接接受基本类型,所以我们需要先将基本类型转成包装类传递到Object中
b.包装类为啥转成基本类型:
包装类不能直接使用+ - * /做运算,所以需要先将包装类转成基本类型,才能直接使用+ - * /符号
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1.概述:Integer是int的包装类
2.构造:
a.Integer(String i):i必须是数字格式
b.Integer(int i)
public class Exercise {
public static void main(String[] args) {
Integer integer = new Integer("10");
System.out.println("integer = " + integer);
Integer integer1 = new Integer(100);
System.out.println("integer1 = " + integer1);
Boolean aBoolean = new Boolean(true);
System.out.println("aBoolean = " + aBoolean);
Boolean aBoolean1 = new Boolean("true");
System.out.println("aBoolean1 = " + aBoolean1);
Boolean aBoolean2 = new Boolean("True");
System.out.println("aBoolean2 = " + aBoolean2);
}
}
//输出
integer = 10
integer1 = 100
aBoolean = true
aBoolean1 = true
aBoolean2 = true
Boolean aBoolean = new Boolean(true);
Boolean包装类中 , 底层调用了parseBoolean(String s) , 在parseBoolean中调用了equals.IgnoreCase(s) , 忽略了大小写
public static boolean parseBoolean(String s) { return "true".equalsIgnoreCase(s); }
1.装箱:
将基本类型变成包装类
2.方法:
static Integer valueOf(int i)
static Integer valueOf(String s) -> s需要是数字格式
1.拆箱:将包装类转成基本类型
2.方法:
int intValue()
public class Exercise03 {
public static void main(String[] args) {
//装箱
Integer integer = Integer.valueOf(10);
System.out.println("integer = " + integer);
Integer integer1 = Integer.valueOf("15");
System.out.println("integer1 = " + integer1);
//拆箱
int value = integer.intValue();
System.out.println("value = " + value);
int value1 = integer1.intValue();
System.out.println("value1 = " + value1);
}
}
在操作的过程中,基本类型和包装类之间可以自动转换
public class Demo04Integer {
public static void main(String[] args) {
Integer i = 10;
i+=1;
System.out.println(i);
}
}
反编译后如下图:
笔试题:
public class Demo05Integer { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; System.out.println(i1==i2);//true Integer i3 = 127; Integer i4 = 127; System.out.println(i3==i4);//true Integer i5 = 128; Integer i6 = 128; System.out.println(i5==i6);//false } }
包装类 缓存对象 Byte -128-127 Short -128-127 Integer -128-127 Long -128-127 Float 没有 Double 没有 Character 0-127 Boolean true和false @IntrinsicCandidate public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
可以发现当数值超过了128就会开辟一个新空间 , 即new Integer(i)
此为享元设计模式
1.方式1:
+
2.方式2:String中的静态方法
static String valueOf(int i)
public class String_transform {
public static void main(String[] args) {
//基本数据类型转包装类
//方式1: static String valueOf(int a)
String s = String.valueOf(10);
System.out.println(s+1);
//方式2:+""
int i = 10;
String s1 = i+"";
System.out.println(s1+1);
}
}
每一个包装类中都有一个parseXXX的方法
位置 | 方法 | 说明 |
---|---|---|
Byte | static byte parseByte(String s) | 将字符串转成byte |
Short | static short parseShort(String s) | 将字符串转成short |
Integer | static int parseInt(String s) | 将字符串转成int |
Long | static long parseLong(String s) | 将字符串转成long |
Float | static float parseFloat(String s) | 将字符串转成float |
Double | static double parseDouble(String s) | 将字符串转成double |
Boolean | static boolean parseBoolean(String s) | 将字符串转成boolean |
public class String_transform {
public static void main(String[] args) {
//基本数据类型转包装类
//方式1: static String valueOf(int a)
String s = String.valueOf(10);
System.out.println(s+1);
//方式2:+""
int i = 10;
String s1 = i+"";
System.out.println(s1+1);
//将String转为基本数据类型
int i1 = Integer.parseInt("101");
System.out.println("i1+1 = " + i1+1);
double parseDouble = Double.parseDouble("10.2");
System.out.println("parseDouble+1 = " + parseDouble + 1);
}
}
将来开发,我们定义javabean的时候,需要将基本类型的字段(成员变量)定义成包装类类型的
1.将来我们的javabean都是和表对应的 2.针对下面的Student类,创建出来的表 id -> 一般作为表的主键使用,而且主键会自增 name age 如果主键为自增,我们添加数据时sql语句可以这么写: insert into student (id,name,age) values (NULL,'柳岩',36); 我们可以用NULL占位(主键自增的列) 而我们将来需要将javabean对象中的数据添加到sql语句中,从而保存到数据库中 包装类型的属性默认值为NULL,所以到时候我们将javabean对象中的数据放到数据库中时,我们不需要单独为id属性赋值 如果不给javabean中的id赋值: Student s = new Student(null,"哈哈",36) 此时我们将javabean中的属性值获取出来放到sql中,正好是: insert into student (id,name,age) values (NULL,'哈哈',36);
public class User { private Integer uid; private String name; private Integer age; public User(Integer uid, String name, Integer age) { this.uid = uid; this.name = name; this.age = age; } public User() { } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "uid=" + uid + ", name='" + name + '\'' + ", age=" + age + '}'; } }
进程(Processor) : 程序的一次执行。由操作系统创建并分配资源,执行一个单独的任务。 进程是系统进行资源分配和调度的独立单位,每个进程都有自己的内存空间和系统资源。进程内所有线程共享堆存储空间,保存程序中定义的对象和常量池。
线程作用 : 负责当前进程中程序的运行 , 一个进程中至少有一个线程 . 一个进程中是可以有多个线程的,这样的应用程序就称之为多线程程序 . 线程是进程内的执行单元,不分配单独的资源,执行一个单独的子任务。线程是进程内调度和分派的基本单位,共享进程资源。每个线程有自己的独立的栈存储空间,保存线程执行的方法以及基本类型的数据。 运行的 Java 程序内含至少一个主线程 main ,用户可以在 Java 程序中自定义并调用多个线程。 JVM 垃圾回收线程也是一个独立的线程。
并发:同一个时刻多个线程同时操作了同一个数据 , 多个任务交替使用 CPU 核心工作,以提高 CPU 利用率。
并行:同一个时刻多个线程同时执行不同的程序 , 多个CPU核心同时工作,处理不同的任务。
1.分时调度:指的是让所有的线程轮流获取CPU使用权,并且平均分配每个线程占用的cpu的时间片
2.抢占式调度:多个线程轮流抢占CPU使用权,哪个线程先抢到,哪个线程先执行,一般都是优先级高的抢到CPU使用权的概率大,我们java程序都是抢占式调度
线程的运行状态 : 线程除创建状态 New 和结束状态 Terminate 外,主要有以下几种运行状态:
NEW(新建) : 线程刚被创建,但是并未启动。还没调用start方法。
运行 : (Running) CPU 正在执行线程。
就绪 : (Runnable) 线程一切就绪,等待 CPU 执行。
运行/就绪状态 统称为可运行状态 Runnable。 Java 程序中,线程在 运行/就绪状态 之间的切换由 JVM 自动调度,开发者无法获知。线程之间的调度采用分优先级多队列时间片轮转算法。进程在执行完 CPU 时间片切换到就绪状态之前会先保存自己的状态,下次进入运行状态时再重新加载。
阻塞 : (Blocked) 线程因缺少其他资源,比如请求资源被上锁而暂停执行。在获得资源后进入就绪状态。 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
等待 : (Waitting) 线程接受了等待指令,释放资源暂停执行。在超时/接受唤醒指令后进入就绪状态。 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Terminated(被终止) : 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。或者调用过时方法stop()
在CPU和内存之间专门为main开辟的通道叫做主线程
主线程专门运行main方法的
步骤 :
①定义类 , 继承Thread类(Thread是一个线程类)
②重写Thread中的run方法 , 设置线程任务(自定义的线程需要干什么 , 将业务写进run())
③创建自定义线程类对象
④调用Thread中的start方法(使线程开始执行 ; Java虚拟机会调用线程中的 run 方法)
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("我是MyThread....");
}
}
}
public class Test01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); //开启线程 , jvm自动调用重写的run方法
for (int i = 0; i < 3; i++) {
System.out.println("我是主线程...");
}
}
}
当有多个线程开启时 , 进程会为每个线程独自开启新线程的栈空间
注意 : 同一个线程对象 , 不可以连续调用多个start方法
如果需要开启多个线程 , 需要重新new
1.void start() 开启线程,jvm会自动执行run方法
2.String getName() 获取线程名字
3.static Thread currentThread() -> 获取的是当前正在执行的线程对象,此方法在哪个线程中用,获取的就是哪个线程对象
4.void setName(String name) -> 给线程设置名字
5.static void sleep(long time)->线程睡眠,传递毫秒值,时间超时,线程自动醒来,自动继续执行
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
Thread.sleep(1000l);
System.out.println(getName()+"线程正在执行....");
//getName() 获取线程名字 因为继承了Thread,可以直接调用
}
}
}
public class Test01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); //开启线程 , jvm自动调用重写的run方法
myThread.setName("haha"); //修改线程名字
for (int i = 0; i < 3; i++) {
Thread.sleep(1000l); //线程睡眠1秒
System.out.println(Thread.currentThread().getName()+"线程正在执行..."); //static Thread currentThread() 获取的是当前正在执行的线程对象
}
}
}
//输出
main线程正在执行...
haha线程正在执行....
main线程正在执行...
haha线程正在执行....
main线程正在执行...
haha线程正在执行....
步骤 :
①定义一个类 , 实现Runnable接口
②重写run方法 , 设置线程任务
③创建自定义的线程类对象
④利用Thread中的构造方法 : Thread(Runnable target) , 创建实现类对象 , 放到Thread中
⑤调用start方法 , 启动线程
public class MyThread_implements implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"线程正在执行....");
}
}
}
public class Test02 {
public static void main(String[] args) throws InterruptedException {
MyThread_implements myThread_implements = new MyThread_implements();
Thread thread = new Thread(myThread_implements);
thread.start();
for (int i = 0; i < 3; i++) {
Thread.sleep(100l);
System.out.println(Thread.currentThread().getName()+"线程正在执行...");
}
}
}
问题 : 为什么再run方法中处理异常只能使用try_catch?
因为Thread中的run方法没有抛异常 , 子类重写父类之后也不抛 , 只能try_catch自己解决
继承extends Thread : 由于继承只能单继承 , 如果某个场景它有父类也需要继承 , 就无法继承Thread了 , 所以有局限性
实现implement Runnable : 接口可以多继承多实现 , 解决了继承的局限性 , 一个类继承一个父类的同 时可以实现一个或多个接口 -----> 推荐使用
匿名内部类回顾:一种格式代表子类对象或实现类对象
使用:
new 接口/抽象父类(){
重写方法
}.重写的方法();
接口/抽象类 对象名 = new 接口/抽象父类(){
重写方法
}
对象名.重写的方法();
public class Test03Niming {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"匿名线程正在执行...");
}
}
}).start();
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"主线程正在执行...");
}
}
}
问 : 什么时候出现线程安全问题 ?
答 : 当多个线程访问同一个资源时 , 可能会出现线程安全问题
public class MyTicket implements Runnable{
int ticket = 100;
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
}
ticket--;
}
}
}
public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();
Thread thread1 = new Thread(myTicket,"柳岩");
Thread thread2 = new Thread(myTicket,"涛哥");
Thread thread3 = new Thread(myTicket,"金莲");
thread1.start();
thread2.start();
thread3.start();
}
}
原因 : CPU在多个线程中做高速切换
格式:
synchronized(任意对象->锁对象){
可能出现的线程不安全的代码
}
执行流程:
某一个线程抢到锁,进入内部执行,其他线程就抢不到锁了,只能等待,等着执行的线程出了代码块,将锁释放,其他线程才有可能抢到锁对象,去执行
public class Ticket implements Runnable{
Integer ticket = 50;
@Override
public void run() {
while (true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (this){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket+"张票");
ticket--;
}else{
return ;
}
}
}
}
}
public class Test04 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket, "张三");
Thread t2 = new Thread(ticket, "李四");
Thread t3 = new Thread(ticket, "王五");
t1.start();
t2.start();
t3.start();
}
}
1.格式:
public synchronized 返回值类型 方法名(参数){
方法体
return 结果
}
public 返回值类型 方法名(参数){
synchronized(this){
方法体
return 结果
}
}
2.默认锁:this
public class Ticket1 implements Runnable{
Integer ticket = 50;
@Override
public void run() {
while (true){
try {
Thread.sleep(100l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
method();
if(ticket == 0){
return;
}
}
}
/*
//采用synchronized同步方法
public synchronized void method(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket+"张票");
ticket--;
}
}*/
public void method(){
synchronized (this){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket+"张票");
ticket--;
}
}
}
}
public class Test05 {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
Thread t1 = new Thread(ticket1, "张三");
Thread t2 = new Thread(ticket1, "李四");
Thread t3 = new Thread(ticket1, "王五");
t1.start();
t2.start();
t3.start();
}
}
1.格式:
public static synchronized 返回值类型 方法名(参数){
方法体
return 结果
}
2.默认锁:
当前类.class
public class Ticket2 implements Runnable{
static Integer ticket = 50;
@Override
public void run() {
while (true){
try {
Thread.sleep(100l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
method();
if(ticket == 0){
return;
}
}
}
/*
//采用synchronized同步方法
public static synchronized void method(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket+"张票");
ticket--;
}
}*/
public static void method(){
synchronized (Ticket2.class){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket+"张票");
ticket--;
}
}
}
}
public class Test06 {
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
Thread t1 = new Thread(ticket2, "张三");
Thread t2 = new Thread(ticket2, "李四");
Thread t3 = new Thread(ticket2, "王五");
t1.start();
t2.start();
t3.start();
}
}
指的是两个或者两个以上的线程在执行的过程中,由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况就称之为死锁.
根据上图所示:线程T1正在持有R1锁,但是T1线程必须再拿到R2锁,才能继续执行
而线程T2正在持有R2锁,但是T2线程需要再拿到R1锁,才能继续执行
此时两个线程处于互相等待的状态,就是死锁,在程序中的死锁将出现在同步代码块的嵌套中
public class LockA {
public static LockA lockA = new LockA();
}
public class LockB {
public static LockB lockB = new LockB();
}
public class DieLock implements Runnable {
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockA.lockA) {
System.out.println("if...lockA");
synchronized (LockB.lockB) {
System.out.println("if...lockB");
}
}
} else {
synchronized (LockB.lockB) {
System.out.println("else...lockB");
synchronized (LockA.lockA) {
System.out.println("else...lockA");
}
}
}
}
}
public class Test01 {
public static void main(String[] args) {
DieLock dieLock1 = new DieLock(true);
DieLock dieLock2 = new DieLock(false);
new Thread(dieLock1).start();
new Thread(dieLock2).start();
}
}
lag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockA.lockA) {
System.out.println("if...lockA");
synchronized (LockB.lockB) {
System.out.println("if...lockB");
}
}
} else {
synchronized (LockB.lockB) {
System.out.println("else...lockB");
synchronized (LockA.lockA) {
System.out.println("else...lockA");
}
}
}
}
}
~~~java
public class Test01 {
public static void main(String[] args) {
DieLock dieLock1 = new DieLock(true);
DieLock dieLock2 = new DieLock(false);
new Thread(dieLock1).start();
new Thread(dieLock2).start();
}
}