一、JDK1.8内存结构
这里介绍的是JDK1.8 JVM内存模型。1.8同1.7比,最大的差别就是:元数据区取代了永久代(方法区)。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存
1.程序计数器
每个线程一块,指向当前线程正在执行的字节码代码的行号。如果当前线程执行的是native方法,则其值为null。
(程序计数器 一般不需要我没关心的)
2.Java虚拟机栈
线程私有,生命周期与线程同进同退。每个Java方法在被调用的时候都会创建一个栈帧,并入栈。一旦完成调用,则出栈。所有的的栈帧都出栈后,线程也就完成了使命。
在程序执行过程中,把方法加载到内存中(这块内存我们叫 栈内存,所以我们的局部变量就存储在栈空间)
3.本地方法栈
功能与Java虚拟机栈十分相同。区别在于,本地方法栈为虚拟机使用到的native方法服务不需要关心,当Java语言调用 windows系统中的 c语言方法的时候 ,把C语言加载到本地方法栈
4.堆 (重点)
此内存区域的唯一作用就是用来存放对象实例,几乎所有的对象实例都在这里分配内存。堆是java虚拟机管理的内存中最大的一块,被所有线程共享。由于堆是垃圾收集器管理的主要区域,所以也被称为GC堆。由于现代收集器基本都采用分代收集算法,所以java堆中还可以细分为新生代,老年代。新生代又细分为Edan区、From Survivor区(S0)、To Survivor区(S1)
JDK1.8里面 ,把常量也存储到堆区
5.元数据区
存储已被虚拟机加载的类信息。随着JDK8的到来,JVM不再有方法区(PermGen),原方法区存储的信息被分成两部分:1、虚拟机加载的类信息,2、运行时常量池。分别被移动到了元空间和堆中
存储的是class文件
二、数组
1. 数组的概念与特点
public static void main(String[] args) {
//定义一个学生的名字
String name1 = “徐家豪”; //字符串常量
//…. 定义55位同学的名字 就要创建55个变量存储 55位同学的名字
String name55 = “李杰”;
//定义一个变量 存储55位同学的名字<br /> //数组就是主要一个大容器 能存储很多的数据
System.out.println(name1);//0x123<br />}<br />数组的目的就是解决存储 很多个数据<br />数组:是一种存储空间是连续的,元素类型是一致的 这么一个装东西的容器。并且这个容器可以使用下标,或者叫索引的东西,可以去进行检索
- 数组可以连续的存储很多个数据
- 容器中必须定义存储的是什么 的数据 (数据类型)
- 数据可以从容器中取出
例如 :存储学生的姓名 (55名学生)
定义一个数组,类型是String类型 ,存储容量是55
String[] names = new String[55];
2.数组的声明和赋值
数组的声明 :语法
类型[] 变量名 = new 类型[数组的容量];
//定义数组时 必须定义容量
定义数组时 分为两部分 :
- 数组的声明 //声明一个数组 存储10个数字
int[] nums ; //定义了一个数组的变量 - 数组的赋值数组的赋值分为两种 :
- 静态赋值int[] nums ;
nums = {10,20,30,40,50,60,70,80,90,100}; //静态赋值
//int[] nums = {10,20,30,40,50,60,70,80,90,100};
- 静态赋值int[] nums ;
int datas[] = {1,4,5,7,8,34,67,89,100} ;静态赋值的时候,不需要考虑数组的容量 ,你给多少个值 就是多大容量
- 动态赋值动态赋值 ,先把数组定义好,并且初始化好 ,然后利用数据的下标进行赋值int[] scores = new int[10];
//数组的下标是从0开始
scores[0] = 89;
scores[1] = 78;
//。。。
scores[9] = 65;- 数组中数据的获取在获取数组中值的时候, 使用下标进行获取//定义一个数组 存储学生的成绩
double[] scores = new double[5];
scores[0] = 89;
scores[1] = 67;
scores[2] = 87;
scores[3] = 45;
scores[4] = 79;
//scores[5] = 100; 超出容量
- 数组中数据的获取在获取数组中值的时候, 使用下标进行获取//定义一个数组 存储学生的成绩
double x = scores[1];
定义一个数组 ,存储5个城市的名字 然后把5个城市的名字获取到 并输出
public class Test02_数组的取值 {
public static void main(String[] args) {
//定义一个数组 ,存储5个城市的名字 然后把5个城市的名字获取到 并输出
String[] citys = {“北京”,”南京”,”上海”,”东京”,”深圳”};
System.out.println(citys[0]);
System.out.println(citys[1]);
System.out.println(citys[2]);
System.out.println(citys[3]);
System.out.println(citys[4]);
//System.out.println(citys[5]); 超出边界
/
java.lang.ArrayIndexOutOfBoundsException 数组越界错误
*/
System.out.println(“============================”);
String[] pros = new String[5];
pros[0] = “江苏省”;
pros[1] = “湖南省”;
pros[2] = “江西省”;
pros[3] = “浙江省”;
pros[4] = “山东省”;
//pros[5] = “上海市”; //越界
System.out.println(pros[3]);<br /> }<br />}
3.数组的使用
如何获取一个数组的元素个数 (数组的容量):
String[] citys = {“北京”,”南京”,”上海”,”东京”,”深圳”};
//数组中有一个属性 length
System.out.println(“citys数组的长度:”+citys.length);
public class Test03_数组的遍历 {
public static void main(String[] args) {
String[] citys = {“北京”,”南京”,”上海”,”东京”,”深圳”};
//循环遍历打印这个数组
for(int i=0;i
}
System.out.println(“======”);
// 定义一个数组 存储学生的成绩 10 个成绩 然后再把学生成绩 循环遍历出来
int[] scores = new int[10];
Random r = new Random();
for(int i=0;i
}
//打印出所有成绩
System.out.println(“所有学生成绩:”);
for(int i=0;i
}
//打印出 及格学生的成绩
System.out.println(“及格学生的成绩:”);
for(int i=0;i
System.out.print(scores[i] + “ “);
}
}
//计算出 所有学生的平均分
int sum = 0;
for(int i=0;i
}
System.out.println(“品均分:”+sum / (scores.length * 1.0));
//输出 最高成绩 和 最低成绩
int max = 0; //假设是最高成绩
int min = 100; //假设是最低成绩
for(int i=0;i
max = scores[i] ;
}
}
System.out.println(“最高分是:”+max);
}
}
案例 :在数组中查询一个数据 ,如果查询到打印出来 ,查不到提示查不到
4.数组内存分析
在Java中如果是基本数据类型 ,存储在栈区 ,因为基本数据类型 在Java虚拟机中已经被定义好 。
引用类型:必须是用户自定义 ,在虚拟机中存储 ,存储在堆区,然后使用的时候,使用的是引用类型的 引用(变量名字称),通过引用在堆区找到引用对象 ,所以在引用上存储的是引用对象的地址。
数组就是一个引用类型 :
定义一个数组:
//静态赋值
int[] nums = {1,4,23,56,66};
//动态赋值
int[] ages = new int[5];
ages[0] = 18;
ages[3] = 20;
//日期对象数组
Date[] dates = new Date[5];
dates[0] = new Date();
dates[2] = new Date();
5.数组的小算法
- 浅拷贝地址的复制两个数组的引用 指向同一个内存地址
改变任何一个数组的元素值,另外一个数组会变化,因为他们指向的是同一个数组public class Test05_数组拷贝 {
public static void main(String[] args) {
//数组的浅拷贝
int[] nums = {1,2,3,4,5} ; // 0x123 nums = 0x123
int[] ms = nums ;//浅拷贝
nums[2] = 100 ;
System.out.println(ms[2]);
}
} 深拷贝 在内存中在拷贝一个数组对象 ,把元数组的元素数据 ,转到新数组中去 ,两个数组互补影响
public class Test05_数组拷贝 {
public static void main(String[] args) {
//数组的深拷贝
int[] nums = {1,2,3,4,5} ; // 0x123 nums = 0x123
int[] ms = new int[nums.length];
for(int i=0;ims[i] = nums[i];
}
nums[2] = 1000;
for(int i=0;iSystem.out.println(ms[i]);
}
}
}冒泡排序是排序算法的一种 int[] nums = {2,4,12,7,56,45,23,99,77} ;
int temp = 0 ; //定义一个临时变量 用于数据交换
for(int i=0;ifor(int j=0;j if(nums[j] > nums[j+1]){
//如果前面的数据比后面的数据大 ,数据交换
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}加强for循环 : 和之前下标循环(普通for循环达到的功能是一样的,目的简化循环操作)语法 :/
类型 : 数据的类型
变量 : 循环出来的每一个数据 自定义命名
容器 :循环的目标
/
for (类型 变量 : 容器(数组、集合)) {
// 循环体
}
for(int i :nums) {
System.out.println(i);
}
String[] names = {“jim”,”jack”,”张三丰”,”张无忌”,”赵敏”};
for(String s : names){
System.out.println(s);
}不能直接获取到下标
- 选择排序 public class Test02_选择排序 {
public static void main(String[] args) {
int[] nums = {12,4,77,56,34,88,74,23} ;
//找到此数组最大数据所在的下标
/int max = 0 ;//假设最大数的下标就是0
for(int i=1;iif(nums[max] < nums[i]){ /
max = i;
}
}
System.out.println(nums[max]);
int temp = 0;//临时存储变量
for(int i=0;iint max = i ; //假设这是最大数据的下标
for(int j= i+1;jif(nums[max] > nums[j]){
max = j;
}
}
//判断是否需要交换数据
if(max != i){
temp = nums[max];
nums[max] = nums[i];
nums[i] = temp;
}
}
for(int i :nums){
System.out.println(i);
}
}
} 二分查找二分查找是一种快速查询数据的方式 ,但是这个数组或者集合 必须是经过排序的public static int getIndex(int[] nums ,int data){
int left = 0;
int right = nums.length-1;while(left <= right){
int mid = (left + right) / 2 ;if(nums[mid] == data) {<br /> return mid;<br /> }else if(nums[mid] > data){<br /> right = mid-1;<br /> }else if(nums[mid] < data){<br /> left = mid+1;<br /> }<br /> }
三、二维数组
二维数组是程序员给起的名字。
二维数组的意思是 数组元素中存储的也是一个数组
int[] nums = {2,3,5,6,8,9};// 一般一维数组
int[][] datas = {{1,2,3} ,{4,5,6} ,{6,7,8},{,1,22,33,44,55}}
二维数组在内存的存储 :
二维数组的定义:静态赋值int[][] datas = {{1,2,3} ,{4,5,6} ,{6,7,8},{,1,22,33,44,55}}
- 动态赋值int[][] datas ; //二维数组的声明 null;
datas = new int[4][]; //在定义二维数组的时候 一位一定要给长度 二维中不需要制定长度
// int[][] datas = new int[4][];
//如何给二维数组赋值
datas[0][0] = 10;
datas[0][1] = 20;
datas[0][2] = 30;
// 上述赋值完之后 ,datas数组的 0号位上 被赋予了值 这个值是一个 数组 {10,20,30} 其他号位 是 null 值
datas[1] = new int[3] ;
datas[1][2] = 201;3.二维数组的遍历int[][] datas = {{1,2,3} ,{4,5,6} ,{6,7,8},{1,22,33,44,55}} ;//遍历这个数组
public class Test04_二维数组 {
public static void main(String[] args) {
int[][] datas = {{1,2,3} ,{4,5,6} ,{6,7,8},{1,22,33,44,55}} ;//遍历这个数组
for(int i=0;i<datas.length;i++) {<br /> //获取的每一个 i 上值 都是一个 数组<br /> for(int j=0;j<datas[i].length;j++){<br /> System.out.print(datas[i][j] + " ");<br /> }<br /> <br /> System.out.println();<br /> }<br /> System.out.println("======================");<br /> //定义一个二维数组 数组中存储 每一个省份的城市<br /> String[][] citys = {<br /> {"合肥市","芜湖市","蚌埠市","淮北市","淮南市"}, <br /> {"南京市","无锡市","苏州市","常州市","镇江市"}, <br /> {"武汉市","恩施市","黄冈市","襄阳市","孝感市"}, <br /> {"济南市","青岛市","潍坊市","菏泽市","烟台市"}, <br /> {"石家庄市","秦皇岛市","唐山市","承德市","邯郸市"}<br /> };<br /> <br /> for(String[] arr :citys){<br /> for(String s :arr){<br /> System.out.print(s + " ");<br /> }<br /> <br /> System.out.println();<br /> }<br /> }<br />}
只要超过一位的数组 ,都叫多维数组 ,多维数组的本质 就是在数组的元素上存储的依然是一个数组下面以三位数组为例 :三位数组的定义 :
因为数组中有null 值 ,所以循环的时候 一定要判断 数组中的元素不能是null 值 ( 不是最后一维 )因为null 值 不能调用属性和方法 ,紫瑶使用null调用属性和方法 就会报 空指针异常public class Test05_多维数组 {
public static void main(String[] args) {
String[][][] areas = {
{{“合肥一区”,”合肥二区”,”合肥三区”},{“芜湖一区”,”芜湖二区”}},
{{“南京一区”,”南京二区”,”南京三区”}, {“工业园区”,”姑苏区”,”吴中区”,”吴江区”}}
};
for(int i=0;ifor(int j=0;j for(int k=0;k System.out.print(areas[i][j][k] + “ “);
}
System.out.println();
}
System.out.println();
}
//动态赋值
int[][][] nums = new int[3][][];
nums[0] = new int[2][];
nums[0][0] = new int[3];
//赋值
nums[0][0][0] = 1000;
//必须对数组进行初始化 创建对象
nums[2] = new int[3][];
nums[2][2] = new int[3];
//赋值
nums[2][2][2] = 500;
for(int i=0;iif(nums[i] != null){ //判断不是null 值
for(int j=0;jif(nums[i][j] != null) { //判断不是null 值
for(int k=0;kSystem.out.println(nums[i][j][k]);
}
System.out.println();
}
}
System.out.println();
}
}
}
}四、多维数组
- 静态赋值String[][][] citys = {
{{“合肥一区”,”合肥二区”,”合肥三区”},{“芜湖一区”,”芜湖二区”}},
{{“南京一区”,”南京二区”,”南京三区”}, {“工业园区”,”姑苏区”,”吴中区”,”吴江区”}}
}; - 动态赋值
- 静态赋值String[][][] citys = {
五、数组与一般应用变量的区别
public class Test01 {
public static void main(String[] args) {
String str1="A";
String[] st1= {"a","A"};
String[] st2=new String[2];
//比较字符串和数组调用方法的返回的区别
//字符串
System.out.println("字符串");//便于区分
System.out.println(str1);//A
String str2=change(str1);//A
System.out.println(str1);//Aa
System.out.println(str2);
//字符串数组
System.out.println("字符串数组");//便于区分
System.out.println(st1);//地址 [Ljava.lang.String;@6504e3b2
for(String i:st1) {//a,A
System.out.print(i+" ");
}
System.out.println();
st2=changeArr(st1);
System.out.println(st1);//地址 [Ljava.lang.String;@6504e3b2
for(String i:st1) {//A,a
System.out.print(i+" ");
}
System.out.println();
System.out.println(st2);//地址 [Ljava.lang.String;@6504e3b2
for(String i:st2) {//A,a
System.out.print(i+" ");
}
}
public static String change(String str) {
str+=(char)(str.charAt(0)+32);
return str;
}
public static String[] changeArr(String[] st) {
String temp;
temp = st[0];
st[0] = st[1];
st[1] = temp;
return st;
}
}
可以得出数组不经过特殊处理是在原数组上进行修改,而变量则会拷贝一份新的数据在进行操作