疯狂java学习笔记(一)

疯狂java学习笔记(一)

本系列是基础知识巩固,主要参考李刚老师的疯狂java 突破程序员基本功的16课这本书籍的学习笔记总结。

  1. 数组的特性
  2. 对于堆栈池的研究
  3. 对于特殊字符串String的理解

一、数组的特征

1.数组是静态的

java本身是一种静态的语言,在java中数组当然也是静态的,是可以保存一组数据的一种数据结构,它本身也会占用一个内存地址,因此数组是引用类型。

这里引出一个静态语言与动态语言的概念:
动态类型语言:指在运行期间才去做数据类型检查,也就是说,用动态语言编程时,永远不用去给任何变量去指定数据类型。该语言会在你第一次给该变量赋值的时候,在内部把数据类型记录下来。比如ruby或者Python是典型的动态类型的语言,很多的脚本语言也属于动态语言,其实很大的特征是这些语言,在声明变量的时候,并不像静态语言那样对数据类型要求特别严格。
静态类型语言:指在编译期间就去做数据类型检查,也就是说在编码时要声明数据类型。 java和c、c++都是静态类型的语言。

2.数组在使用时必须初始化

初始化方法有静态动态两种:
静态初始化是指明内容由系统根据内容指定大小。
动态指定大小根据后续操作填入内容。

// 静态初始化方法
String name1[] = new String[] { "张三", "李四", "王五" };
String name2[] = { "张三", "李四", "王五" };
// 动态初始化方法
String name3[] = new String[3];

数组大小在初始化完成后就是确定的,是不可变的。因为数组在确定自己的大小之后,会向堆内存申请一组连续的,固定大小的空间用于储存数据。和LinkList这种链表式储存结构不同,链表储存是实现了逻辑上的连续,内存空间上不连续,LinkedList一个数据节点在储存了自己的数据的同时,还要储存下一个数据节点的地址信息,在访问的时候实现逻辑上的连续。

这里引出另一种常用List:ArrayList:
既然LinkedList是链表,所以能够实现动态的增加,但是我们最常用的ArrayList呢?没错,我们常用的列表ArrayList,底层的实现其实是数组,但是ArrayList之所以能够动态的增长,其实是一个伪动态,在上面我们说过数组大小在初始化完成后就是确定的,是不可变的,ArrayList在创建的时候,申请堆内存数据时会缓存一定的大小(一般默认为10),在储存完10个数据后不会申请新的内存,当在储存第11个数据的时候,则会像内存重新申请一个新的数组,其大小根据ArrayList源码可知是在原有的大小上*3/2+1,也就是16,然后将原数组数据转移到新的数组中来,从而实现动态的增长。

3.变量数组和数组的值之间的关系

首先数组对象是一个变量,这个变量是储存在栈中,这个栈中的数组本身是储存在堆中,在数组初始化时向堆内存申请开辟一片连续的固定大小的空间用于储存数据,栈中的数组对象储存堆中真实数组的地址信息(也就是引用类型变量),如果堆中数组里的数据信息如果储存的数据类型是基本类型变量,则会直接储存在对应的内存空间中,如果变量类型是引用类型则会指向堆内的具体对象上(储存堆中的对象地址,数组本身也是引用类型)。

二、对于堆栈池的研究

1.栈

栈的本质实际上就是一张线性表,线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享。

java中栈分为两种,一种是JVM运行使用的全局栈,和在运行代码的方法时生成的方法栈
全局栈是JVM运行时控制线程所使用的栈,在这里不深入了解,我们正常所说的栈为方法栈,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,也就是我们所说的局部变量,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束。这也是局部变量只能在方法中有效的原因。

2.堆

堆数据结构是一种数组对象,它可以被视为一科完全二叉树结构。由于二叉树储存的特征,可以随便的向下添加节点,导致堆特别适合储存java中的对象。

java编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要创建一个对象的时候,只需要 new 写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价:用堆进行存储分配比用堆栈进行存储存储需要更多的时间。

3.池

池分为两种,静态常量池运行时常量池
静态常量池:也称作静态域,静态常量池里面储存着类、方法信息,如果将某个变量定义为static,那么这个类及其所有对象共享同一个值,它在内存中只存在一份,严格来说是在每个JVM中只存在一份。 这种定义了的全局变量就储存在静态域中。

运行时常量池:我们一般所说的常量池,java中的常量池技术,是为了方便快捷地创建某些对象而出现的。常量池其实也就是一个内存空间,通俗点就是java级别的缓存技术,方便快捷的创建一个对象。当需要一个对象时,从池中去获取(常量池还具备动态性,运行期间如果发现池中没有,就创建一个并放入池中),当下次需要相同变量的时候,不用重新创建,从而节省时间空间(基本是缓存基本类型数据和String)。

4.数据在堆栈池中的体现

静态全局变量:存放在静态常量池中,所有方法都可以访问他(所以一般定义时都会加上final随意更改的话会很不好控制)。
全局变量:存放在堆中,在对象被创建的同时被创建。
局部变量:存放在方法栈中,随着方法执行被创建,随着方法执行完之后,方法栈被销毁而被销毁。

三、对于String特殊类型内存储存的研究

上面我们提到常量池用来生成基本数据类型和String类型,然而基本数据类型直接存在堆内存或者栈中储存,但是String呢 String并不属于基本数据类型而是属于引用数据类型,他的储存方式又是什么样的呢?
打开String的源码,类注释中有这么一段话“Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.”。这句话总结归纳了String的一个最重要的特点:String是值不可变的常量,是线程安全的。接下来,String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。

String的创建一共能分成三种模式

String s1 = new String("myString");
String s2 = "myString";
String s3 = "my" + "String";

第一种是使用String对象的方式,首先String字符串创建时在常亮池中完成的,最终一定指向常亮池的这一串字符串,使用对象的方式创建的结果就是 Sting s1 s1这个变量在栈中,引用类型变量创建了一个String对象,这个String对象是在堆中的,这个堆中的String对象最终又指向池中具体的“myString”这一串字符串。
第二种则是,跳过堆中的对象,直接栈中对象指向池中的字符串。
第三种比较特殊,如果按照这种写法,java程序会先帮你拼接成“myString”然后再存入池中,实际上池中只创建了一次字符串。
特别的讲一下字符串的拼接 如果是如下格式进行字符串拼接的内存变化过程

String s1 = "my";
String s2 = "String";
String s3 = s1 + s2;

首先常量池创建“my”给s1,创建“String”给s2,创建s3时,将s1和s2的数据取出,拼接好后,再创建“myString”给s3。

在这里,出现了面试考官百出不厌的问题,字符串==与equals的花式比较,这里就具体讲解一下:
equals和==都是比较两个值是否相等所用到的操作,当比较基本数据类型数据时比较其值,当比较引用类型数据时比较其对象是否指向一个对象。我们可以打开Object类,发现 equals底层实现其实就是==,但是String类对equals方法进行了重写,变成了比较其字符串是否相等。知道这些之后基本这些字符串的问题也就一目了然了。下面是我整理的一些结果可供参考:

String s1 = "my";
String s2 = "String";
String s3 = s1 + s2;
String s4 = "myString";
String s5 = "my"+"String";
String s6 = new String("myString");
String s7 = new String("myString");
System.out.println(s3==s4);//false
System.out.println(s4==s5);//true
System.out.println(s5==s6);//false
System.out.println(s5.equals(s6));//true
System.out.println(s6==s7);//false
System.out.println(s6.equals(s7));//true

注意第一组比较 s3=s1+s2;这个过程实际上是s1和s2两个对象相加,实际上s3是需要创建String对象的 和s5并不一样。

以上就是我对疯狂java的第一课的学习笔记,如有错误的地方望指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值