JAVA 必须掌握技能(三)-Java 基础知识

版权声明:本文为CSDN博主「iswitched」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44873106/article/details/89787021

1 环境变量配置

JAVA_HOME:jdk路径
Path:要把jdk的bin目录路径,添加到path变量

2.八种数据基本类型

比较简单此处不单独罗列
引用数据类型:数组,类,接口
2.1 char :Unicode编码的字符,或字符的整数编码,必须用单引号
  float默认值是0.0f;
  double默认值是0.0d;
2.2基本类型字面值规则
  1.整数字面值是int类型,如果右侧赋值超出int范围,需要做转型处理
  2.byte,short,char 三种比int小的整数,在自身范围内可以直接赋值。 byte d=1+3 正确,1+3编译器会自动转成4
  3.浮点数字面值是double;浮点数转成整数会直接舍弃小数点后位数。
  4.字面值后缀,L D F
  5.字面值前缀,0b 二进制;0x 16进制;0 8进制; u char 类型16进制
2.3基本类型的运算规则
  1.计算结果的数据类型与运算中的最大类型一致。
  2.byte,short,char三种比int小的整数,计算时会自动转成int 做加法运算时,数据类型会自动转成int,除了自增加自减不进行转化外,其它情况都是无long型时,所有非int类型转成int类型;有long类型时,都转成long类型。 char类型相加,提升为int类型。   

  3.整数运算溢出。Integer.MAX_VALUE+1 得负数最小值
  4.浮点数运算不精确
  5.浮点数特殊值 infinity 整数除0 ;Nan 负数开方
2.4 基本类型的类型转换
  数字类型之间可以互相转换,从小到大自动转换,从大到小需要强制转型。
  double d = 245; float d=100;自动转型

2.5运算符
  && :逻辑与(短路与),两边同为真结果才为真,短路与:左边是假,右边忽略不执行
  & :不管左边结果是什么都要执行右边(&的左右两边都要参与运算)
  || :逻辑或(短路或),两边只要有一个真结果就是真,短路或:左边是真,右边忽略不执行

3.流程控制

3.1 switch:只能判断byte short char int enum jdk1.7之后的string。
  从成立的case 无条件穿透所有的case包括default直到结束或者遇到break中断跳出循环;
  如果所有条件都不成立,则执行default

3.2 for循环

3.3 break 和 continue
  Break 中断、跳出循环和switch
  Continue 跳过后面的代码 继续进入循环的下一轮执行
3.4 for-each循环

  数组遍历、集合迭代遍历的语法简化

4.面向对象 —— 封装、继承、多态 ★ ★ ★ ★ ★

封装
  1 类:模板、图纸 。类中定义对象的属性数据(成员变量),方法(成员方法)类第一次使用时会加载到方法区
  2 对象:从模板中创建的具体实例,实例是数据的打包
    新建实例时,在堆内存中新分配内存空间给这个实例

  3引用变量:理解成“遥控器”,保存一个实例的内存地址(引用变量保存在栈),引用变量的特殊值:null 不保存任何实例的内存地址

  4构造方法:新建实例对象时,立即执行的一个特殊方法;构造方法必须和类同名,并且没有返回值类型。
    一个类中必须有构造方法,自己没定义,系统会添加默认构造方法,构造方法一般用来给属性赋值

  5 构造方法重载

  一个类中可以定义多个不同参数的构造方法,是方法重载的一种体现

  6 方法重载Overload:

  同名不同参,与返回值类型无关,所有方法都可以重载7 this关键字:this.xxx 特殊引用,引用当前对象的地址
  this(…):构造方法之间的调用,必须是首行代码,如果有多个构造方法,会通过this(…)调取下面的所有构造方法,完成赋值。
  注意this不能在静态方法中使用
继承
Java的继承是单继承多实现,只能继承一个父类(如果不继承其他类,默认继承object类),但可以实现多个接口
  1.不能继承的有:构造方法,私有成员
    过程:先新建父类对象,再新建子类对象,两者作为一个整体对象,调用成员时,先找子类,再找父类
  2.方法重写:override
    继承的方法,在子类中重新定义父类中的方法(只能在子类重写),方法名相同,参数的个数和类型也必须相同,返回值类型也必须相同。
    方法重写返回值类型如果是基本类型应与父类的一致;重写要求方法名完全相同,返回值类型如果是基本类型或无返回值时必须一致。
  3.父类的构造方法
    新建子类对象时会先新建父类对象,也会先执行父类的构造方法
    默认执行父类的无参构造,默认隐含调用super();
    new Student() 默认执行父类无参构造
    new Student(……)默认执行父类无参构造
    手动调用父类的有参构造,super(参数):父类没有无参构造时必须手动调用
  4.super
    Super.xxxx() 方法重写时,调用父类中同一个方法的代码
    Super(参数) 调用父类的构造方法,默认调用父类无参构造super(),手动调用有参构造super(),必须是首行代码
    注意super不能在静态方法中使用
多态
一个对象具有多种形态的表现,多态的前提是必须有继承。
void f(父类型 o1) { }
把一个子类型的实例当做父类型来处理,所有的子类型都可以传递到该方法,被当做父类型处理;作用:一致的类型
1. 类型的转换
  A. 向上转型
    子类的实例转成父类型,用父类型的引用变量,来引用子类实例,向上转型后,只能调用父类定义的通用成员,子类特有成员被隐藏
  B. 向下转型
    已经转成父类型的子类实例,转回子类型为了对子类型进行特殊处理
2. Instanceof 运行期类型识别
  当多种子类型都被当做父类型来处理,要对某种子类型进行特殊处理,可以先判断其真实类型再向下转型——对真实类型,及其父类型判断,都返回true。格式:

s instanceof Line 

5.数组
  属于object类,用来存放一组数据的数据结构,数组是最基本的一种数据结构但不是基本数据类型,数组是相同数据类型组成的集合,数组中的元素按线性顺序排序
1 数组的创建
  数组创建后若未指定初始值,则会依据数组类型的不同来设置默认值

int[] a = new int[6];
// 新建int[]数组,长度6,默认值都是0,数组的起始地址值保存在变量a。

int[] a = {6,2,6,8};
Int[] a= new int[]{1,2,3,4,5};
a = new int[]{7,3,8,1,7,9,3,1};
// 为存在的数组变量赋值直接初始化数据,要添加数据类型

2 数组的长度属性 a.length
  数组一旦创立,长度不可变
  最大下标 a.length-1
  允许0长度的数组
3 二维数组
  存放数组的数组

int[][] a = new int[3][2];

外围长度为3,内部3个数组长度为2,一共有4个数组,内部数组默认值0,外围数组保存的是内部数组的地址。

int[][] a = new int[3][];

只建一个外围数组长度3,3个位置都是null,之后可以建新数组放入内部。

4 Arrays 数组工具类

Arrays.toString //(数组) 把数组数据连接成字符串。
Arrays.sort //(数组) 数组排序 基本类型:优化的快速排序;引用类型:优化的合并排序。
Arrays.binarySearch //(数组,目标值) 二分法查找,在有序数组中查找目标值下标,找不到返回 -(插入点+1)。
Arrays.copyof // (数组,长度) 复制数组成一个指定长度的新数组。

5 数组 复制

Arrays.copyof // (数组,长度) 复制数组成一个指定长度的新数组
System.arraycopy // (原数组,原数组起始位置,目标数组,目标数组起始位置,复制的数量) ——不会创建新的数组,目标数组要事先存在。

6. 变量

// 1 局部变量:定义在方法中或局部代码块中,必须初始化(第一次赋值时分配内存空间)
// 局部变量的作用域在定义它的大括号内有效,在作用范围内不能重复定义。
// 2 成员变量:定义在类中,自动初始化默认值,访问受访问控制符限制;局部变量可以和成员变量同名。

7.Object类

如果一个类不继承其他类,则默认继承Object类
1.方法

toString() // 获得一个对象的字符串表示。
// Object中的默认实现是:“类名@地址”可在子类中重写toString方法。
equals() // 当前对象与参与对象比较是否相等。
a.equals(b) // Object中的默认实现是比较内存地址。
// this == obj:==
// Object中比较内存地址,基本类型默认比较内容值。

8.String 类

String是封装char[] 数组的对象

S =”abcd”
S={-value:[‘a’,’b’,’d’,’g’]}

1.字符串创建

Char[] a ={‘a’,’b’,’c’};
String s = new String(a); >>>简易语法>>> String s = “abcd”

2.字符串的常量池

String s1 = “abcd”  // 字符串的字面值写法。第一次使用一个字符串字面值时,会在字符串常量池中新分配内存,再次使用相同字面值时,直接访问常量池中存在的对象,而不会重复创建

3.字符串 中的Equals 和 “==”

// “==”比较内存地址
// Equals 看父类中的方法,object中的默认方法是比较内存地址,String类中重写了父类方法比较的是字符内容。如下说明:

char[] a = {'a','b','c','d'};
String s1 = new String(a);//堆中新分配内存
String s2 = "abcd"; //在常量池新分配内存
String s3 = "abcd"; //访问常量池中存在的对象
System.out.println(s1==s2); //false 比较内存地址
System.out.println(s2==s3); //true 比较内存地址
String类中重写了equals方法,方法中比较的是字符内容
System.out.println(s1.equals(s2));//true 比较字符串内容
System.out.println(s2.equals(s3));//true 比较字符串内容

4.字符串不可变且字符串连接效率低,每次连接都会新建字符串对象
5.字符串的常用方法

charAt(i)  // 获取指定位置的字符
length()  //  字符串长度,字符的数量
indexof()  // 找第一个子串出现的初始位置,找不到返回-1
indexof  // (子串,start)从执行位置向后找
lastIndexof(子串)   // 从后向前找
subString(start)  // 截取start到末尾
subString(start,end )  // 截取(start,end )范围
trim()  // 去除两端的空白字符
matches()  // 用来判断是否匹配正则表达式

6.StringBuilder: 可变的字符序列,封装char[]数组,提供了一组方法,可以对内部封装的字符进行修改,常用来代替字符串做高效的字符串连接

append()//  追加字符内容,内部数组默认初始容量16,放满后翻倍+2;
delete(start,end) //  删除区间(start,end);
deleteCharAt(i)//  删除指定位置 i;
insert(i,内容) //  在指定位置插入内容;
insertCharAt(i,字符)//  在指定位置插入单个字符;
replace(start,end,内容)//  替换指定范围的内容;

//  StringBuilder和StringBuffer
//  StringBuilder:线程不安全,效率高;JDK1.5版本后的新类。
//  StringBuffer:线程安全,旧版本的类。

9.正则表达式
一般用来判断用户的输入内容是否符合格式要求

matches()// 字符串的方法,用来判断是否匹配
if(s.matches(regex)) {}
split(正则):// 用匹配的子串来拆分字符串
String s = "aaa,bbb,ccc";
String[] a = s.split(",");

replace(正则,子串)替换所有匹配的子串

10.基本类型的包装类

// 把基本类型当做对象来使用
byte –    Byte
short –    Short
int –    Integer
long    –    Long
float    –    Float
double –    Double
char    –    Character
boolean –    Boolean

1. 数字父类Number

// 子类:Byte,Short,Integer,Long,Float,Double,BigDecimal,BigInteger
// 取出基本类型值的方法
byteValue(),shortValue(),intValue(),longValue(),floatValue(),doubleValu()

2. Intger类

// 创建Integer对象: a= { value:6}
Integer a = new Integer(6);
Integer a = Integer.valueOf(6);
// Integer 类中存在256个Integer缓存对象,封装-127到128;如果访问指定范围内的值,会访问缓存对象,如果超出范围,会新建对象。

3. Integer类的方法

// 字符串解析成int
Integer.parseInt();
Byte.parseByte();
Integer.toBinaryString() // 转成二进制字符串
Integer.toOctalString()  // 转成八进制字符串
Integer.toHexString(255) // 转成十六进制字符串

4. BigDcimal和BigInteger 类
BigDcimal精确的浮点数运算
BigInteger 超大的整数运算

创建对象:

BigDecimal bd = BigDecimal.valueOf(2);
// 方法:
add(BigDecimal bd)
subtract(BigDecimal bd)
multiply(BigDecimal bd)
divide(BigDecimal bd)
divide(BigDecimal bd,保留位数,舍入方式)
setScale(保留位数,舍入方式)

5. 自动装箱,自动拆箱

// 基本类型值,自动装箱成包装对象
// Integer a = 6; 编译器编译成: Integer a = Integer.valueOf(6);

// 自动拆箱(自动拆箱要注意null值)
// int i = a; 编译器编译成:    int i = a.intValue();

11.抽象类
半成品类,没有完成的类;抽象方法:没有代码,只有方法的定义,抽象类不能创建实例,主要用来被继承。

public abstract void f();
//包含抽象方法的类一定是抽象类

public abstract class A { };

抽象方法的作用:
作为通用方法,在父类中定义;要求子类,必须实现这个方法。
1)抽象类可以有自己的构造方法
2)抽象类可以有具体的方法
3)包含抽象方法的类一定是抽象类,必须使用abstract关键字修饰,这个方法必须由子类来实现。
4)抽象类不能使用new关键字来创建实例
5)当一个类中只要有一个抽象方法,这个类就必须是抽象类
6)抽象类可以定义实例变量和静态变量以及常量
7)抽象类可以再继承抽象类,也可以继承普通的类

12.关键字 final
内存地址不可变,可以修饰常量、类、方法
1. final 常量:值不可变,但引用类型因为保存的是地址,所以内容可以变。

final Point a = new Point(3,4);
a.x = 30;//
a.y = 40;//

2. final 方法不能在子类重写,但可以被继承。;final不能用于修饰构造方法,父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
3. final 类 不能被继承,没有子类
Static — 静态 共享的数据
静态成员属于类,而不属于实例

静态成员
用类来调用静态成员 Soldier.count
实例成员
用实例来调用实例成员 s1.id
工具方法
Math.Random() Arrays.toString() String.valueOf()
静态方法中不能直接调用实例的成员(非静态),只能用实例调用

class A {
    public static void main(String[] args) {
        f();//静态调静态
    }
  static void f() {
    g();//错,静态不能直接调用非静态
    A a = new A();
    a.g();//只能用实例调用
  }
  void g(){
  }
}        

静态初始化块

class A {
  static {
    // 静态初始化块
    // 类被加载时,只执行一次
  }
}

静态变量保存在方法区类的空间中,只保存一份可以在所有实例中共享的数据

对象的加载过程
加载类

  • 1.加载父类,为父类静态变量分配内存 – 后台执行不可见
  • 2. 加载子类,为子类静态变量分配内存
  • 3. 执行父类静态变量的赋值运算,和静态初始化块
  • 4. 执行子类静态变量的赋值运算,和静态初始化块

新建实例

  • 5. 新建父类实例,为父类实例变量分配内存
  • 6. 新建子类实例,为子类实例变量分配内存
  • 7. 执行父类的实例变量赋值运算
  • 8. 执行父类的构造方法
  • 9. 执行子类的实例变量赋值运算
  • 10. 执行子类的构造方法

13.集合

用来存放一组数据的数据结构
数组的缺点: 长度固定 ; 访问方式单一只能下标访问; 前面增删数据操作繁琐
集合的继承结构:

  • Collection 是对象集合, Collection 有两个子接口 List 和 Set,
  • List 可以通过下标 (1,2…) 来取得值,值可以重复,而 Set 只能通过游标来取值,并且值是不能重复的
  • ArrayList , Vector , LinkedList 是 List 的实现类
  • ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的
  • LinkedList 是线程不安全的,底层是由链表实现的
  • Map 是键值对集合
  • HashTable 和 HashMap 是 Map 的实现类
  • HashTable 是线程安全的,不能存储 null 值
  • HashMap 不是线程安全的,可以存储 null 值

ArrayList
数组列表,封装了一个数组,及其操作代码和更便捷的方法,内部数组默认初始容量10 放满后,1.5倍增长
方法
add(数据)— 添加数据;get(int i)—访问指定下标数据; remove(int i)移除指定位置数据,返回被移除的数据; remove(数据)— 找到第一个相等的数据,找到移除并返回true,找不到返回false; size() 元素的数量;iterator() 辅助新建迭代器
效率
访问任意位置效率高,增删数据效率可能降低

LinkedList — 双向链表
方法
和ArrayList有相同的方法

  1. LinkedList 两端数据操作方法
  2. addFirst(数据);addLast(数据);getFirst();getLast();removeFisrt()
  3. removeLast()
  4. 效率

两端效率高
HashMap — 哈希表、散列表 (面试必问) ★ ★ ★ ★ ★
存放键值对数据,用键来快速定位数据,来提取键对应的值
键:不重复,无序

  1. Hashmap中的key-value都是储存中entry数组中的
  2. Hashmap的实现不是同步的,意味着它不是线程安全的
  3. Hashmap的实例有两个参数影响其性能:初始容量,和加载因子
  4. 方法
  5. put(key,value)放入键值对数据,重复的键会覆盖旧值
  6. get(key)获得键对应的值,键不存在,得到null
  7. remove(key)移除键值对数据,返回被移除的值
  8. size()键值对的数量

哈希运算过程

HashMap内部,使用entry[]数组存放数据

  • 数组默认初始容量16
  • 放满后容量翻倍+2
  • key.hashCode()获得键的哈希值
  • 用哈希值和数组长度,运算产生下标值 i
  • 新建entry对象来封装键值对数据
  • Entry对象,放入i 位置
  •  如果是空位置,直接放入
  •  如果有数据,一次equals()比较是否相等
  •  找到相等的,覆盖旧值
  •  没有相等的,链表连接在一起
  •  负载率、加载因子超过0.75
  •  新建翻倍容量的新数组
  •  所有数据,重新执行哈希值,存入新数组
  •  Jdk 1.8
  •  链表长度到8,转成红黑树
  •  树上的数据减少到6,转回成链表

hashCode()
hashCode()是object的方法,默认实现是用内存地址作为哈希值
可以重写方法来获得相同的哈希值
哈希运算中要有相同的哈希值,才能保证计算出相同下标值,并且要equals()也要相等(equals方法也要重写),才可以覆盖旧值,否则会链表连接。
重写hashCode的惯用算法:(x.y是变量的值)


14.异常
封装错误信息的对象
错误信息:类型、提示消息、行号
异常的继承结构

捕获异常

try {
  } catch(AException e) {
  } catch(BException e) {
  } catch(父类型Exception e) {
  } finally {
    // 不管出不出错,都会执行
}

如果抛出异常,并且中catch中有return语句,这个return语句会先执行,执行之后将结果保
存在缓存中,再去查看是否有finally,如果有finally就先执行finally语句,之后再返回缓存中
return的值,如果finally中也有return,那么finally中的return会覆盖掉之前缓存中的return,
即最终会返回finally中的return值

throw 手动抛出异常,执行异常的抛出动作 类似 return;当程序出现逻辑错误,不自动创建并抛出异常,可以手动判断逻辑错误,手动创建异常对象并抛出。
底层的异常往上层抛,在上层处理

if(...) {
  AException e = new AException();
  throw e;
}

异常包装

  • 捕获的异常对象,包装成其他类型再做抛出,多种类型简化成一种类型,不能抛出的异常包装成能抛出的异常再抛。
  • RuntimeException 和 其他Exception
  • RuntimeException— 非检查异常,编译器不检查是否有异常处理代码,存在默认的抛出管道
  • 其他异常 — 编译器检查是否有处理代码,不处理,不能编译。

15.接口
极端的抽象类,结构设计工具,用来解耦合,隔离现实
Implements代替extends
Interface 代替class
接口的定义:
 公开的抽象方法  公开的常量  公开的内部类、内部接口

  • 1)接口只能定义常量
  • 2)接口只能定义抽象方法
  • 3)接口只能继承接口,不能继承普通的类和抽象类
  • 4)接口是没有构造方法

注意:

  • 1)在接口中定义常量时,可以不用final static修饰,因为编译器在编译时会自动加上。
  • 2)在接口中定义抽象方法时可以省略abstract关键字,编译器在编译时同样会加上。

类可以同时继承多个接口

class A implements X,Y,Z {}
class A extends B implements X,Y,Z {}

接口和接口的继承

interface A extends X,Y,Z {}

16.文件、字符操作流
File
封装一个磁盘路径字符串,提供了一组对文件、文件夹的操作方法,可以封装文件夹路径、文件路径、不存在的路径。 {path=“d:/abc”}
方法

  • getName() 获取文件名
  • getPatrent() 获取父目录
  • getAbsolutePath()完整路径
  • length() 文件字节量,对文件夹无效,会返回假数据
  • isFile() 判断是否是文件
  • isDirectory()是否是文件夹

创建、删除

  • createNewFile()新建文件,文件已存在不会新建,返回false;文件夹不存在会出现异常
  • mkdirs()逐层创建多层文件夹
  • delete()删除文件、空目录

目录列表

  • list()得到String[] 包含所有文件名 [“a.txt”, “b.mp3”, “c.jpg”]
  • listFiles() 得到 File[],包含所有文件的封装的File对象 [{…}, {…}, {…}]

流 Stream
数据的读写操作(io操作),抽象成数据在管道中流动
单方向流动

  • 输入流,只能用来读取数据(读入内存)
  • 输出流,只能用来输出数据(内存数据向外输出)

只能从头到尾,顺序流动一次,不能反复流动,如果要重复流动,可以重新创建新的流

InputStream,OutputStream
字节流的抽象父类
方法:

  • write(int b) 只输出int四个字节中,末尾的一个字节值 [1][2][3][4] —> [4]
  • write(byte[], start, length) 输出byte[] 数组中,从start开始的length个字节值
  • read() 读取一个字节值,补三个0字节,变成int [4] —> [1][2][3][4],读取结束后,再读取会返回 -1。
  • read(byte[] buff) 按数组的长度,读取一批字节值,存放到指定的数组中,并返回这一批的字节数量,读取结束后,再读取会返回 -1。
  • FileInputStream,FileOutputStream — 文件流
  • ObjectInputStream,ObjectOutputStream —对象序列化、反序列化

序列化 把一个对象的信息,按固定的字节格式,变成一串字节序列输出

方法:

  • writeObject(Object obj) 把对象变成一串字节序列输出
  • readObject() 读取序列化数据,反序列化恢复对象
  • Serializable 接口——被序列化的对象,必须实现 Serializable 接口

不序列化的变量
Static — 属于类,不随对象被序列化输出
Transient —临时,只在程序运行期间,在内存中存在,不会被序列化持久保存
字符编码

  • ASC-II 0到 127,英文、指令字符
  • iso-8859-1 Latin-1 西欧编码 ,把ASC-II扩展到255
  • CJK 编码 亚洲编码,中日韩
  • GBK 国标码 英文单字节,中文双字节
  • Unicode 万国码 常用表,双字节 生僻字 三字节或四字节
  • UTF-8 Unicode 的传输格式 Unicode Transformation format英文,单字节某些字符,双字节;中文,三字节;特殊符号,四字节

Java的char类型是 Unicode
Java的转码运算

InputStreamReader,OutputStreamWriter
字符编码转换流 OutputStreamWriter — 把Java的Unicode编码字符,转成其他编码输出
InputStreamReader —读取其他编码字符,转成Unicode字符

17.内部类

  • 定义在类内部、方法内部或局部代码块内部的类,用来辅助外部实例运算,封装局部数据,或局部的运算逻辑。
  • 非静态内部类、属于实例的内部类 非静态内部类实例,必须依赖于一个外部类的实例才能存在。
  • 静态内部类 静态内部类,与普通的类没有区别。
  • 局部内部类
  • 局部定义的类型,类似于局部变量,有作用范围,只能在局部代码块内使用这种类型

局部内部类中,使用外面的局部变量,必须加 final,jdk1.8,缺省。

匿名内部类

Weapon w = new Weapon() {...};

//{} - 匿名类
//new - 新建匿名类的实例
// Weapon - 父类型
// () - super(),可传参数super(1,2,3)

18.Java内存管理
堆内存 用来存放由new创建的对象实例和数组。Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。
栈内存 保存的是堆内存空间的访问地址,或者说栈中的变量指向堆内存中的变量(Java中的指针)
l 栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。常量池存在于堆中(1.7b后的新版本)。
普通类型的变量在栈中直接保存它所对应的值,而引用类型的变量保存的是一个指向堆区的指针,通过这个指针,就可以找到这个实例在堆区对应的对象。因此,普通类型变量只在栈区占用一块内存,而引用类型变量要在栈区和堆区各占一块内存。
方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

19.线程
在进程内部,并行执行的任务
创建线程(两种方式)

  • 继承 Thread
  • 实现 Runnable

继承 Thread
  编写 Thread 的子类,并重写 run() 方法。启动之后,自动运行 run() 方法中的代码
实现 Runnable
  实现 Runnable 接口,实现它的 run() 方法,Runnable 封装在线程中执行的代码,新建线程对象时,把Runnable对象放在线程内,启动
线程的状态

线程的方法

  • Thread.currentThread() 获得正在执行的线程实例
  • Thread.sleep(毫秒值) 让正在执行的线程,暂停指定的毫秒值时长
  • getName(),setName() 线程名
  • start() 启动线程 interrupt() 打断线程的暂停状态
  • join() 当前线程暂停,等待被调用的线程结束
  • setDaemon(true) 后台线程、守护线程

JVM虚拟机退出条件,是所有前台线程结束,当所有前台线程结束,虚拟机会自动退出
不会等待后台线程结束 例如:垃圾回收器是一个后台线程。
线程同步 synchronized
让多个线程共享访问数据时,步调一致的执行。一个线程修改时,其他线程等待修改完成后才能执行;一个线程访问时,其他线程等待访问结束
任何实例,都有一个“同步锁”,synchronized 关键字,要求一个线程必须抢到同步锁才能执行

synchronized(对象) {
// 共享的数据访问代码
} // --抢对象的锁
synchronized void f() {
} // -- 抢当前实例的锁
static synchronized void f() {
}//  --抢类的锁


生产者、消费者模型
线程之间传递数据
等待和通知方法必须在synchronized 代码内调用, 等待和通知的对象,必须是加锁的对象。

原文地址:https://www.cnblogs.com/blmlove/p/12748197.html