从头开始学Java——Java类型和类

基本数据类型有哪些?为什么需要基本数据类型?

Java中有这么几种基本的数据类型:

  • 整型(byte:8位、short:16位、int:32位、long:64位)、
  • 浮点型(float:32位、double:64位)、
  • 字符类型(char:16位)、
  • 布尔类型(boolean,单个的boolean 类型变量在编译的时候是使用的int 类型。而对于boolean 类型的数组时,在编译的时候是作为byte,因为对于32位的CPU,这样存储是最具备效率的。参考:boolean到底占几个字节)。
  1. 使用基本数据类型,简化了使用对象操作和类型转换的复杂性。
  2. 对象本身来说是比较消耗资源的,对于经常用到的类型,如int等,直接new出来会比较笨重。
  3. 对于基本数据类型,作为局部变量是存储在方法栈中的,作为成员变量是存储在堆上的;对于引用类型,作为成员变量是存储在堆上的,作为局部变量,在方法栈中存了引用,引用指向了堆上的变量(注:类的成员变量或者类的静态变量还是存储在堆里)。

基本类型的转换问题

转换时遵循的原则:

  • byte–>short–>int–>long–>float–>double
  • 容量小的类型自动转换为容量大的类型;
  • 容量大的类型转换为容量小的类型时,要加强制转换符

short s1 = 1; s1 = s1 + 1;语句是错误的,因为s1+1会自动转换为int类型,而int类型被赋值给short类型,是错误的(编译器会报需要强制转换类型的错误)。

对于short s1=1;s1+=1;由于+=是java语言规定的运算符,java编译器会自动的对它进行特殊处理,所以可以正确编译。

为什么需要包装类?什么是自动拆装箱?

因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

有了基本数据类型和包装类,肯定有些时候要在他们之间进行转换。比如把一个基本数据类型的int转换成一个包装类型的Integer对象。
我们认为包装类是对基本类型的包装,所以,把基本数据类型转换成包装类的过程就是打包装,英文对应于boxing,中文翻译为装箱。
Java SE5之前,要进行装箱,可以通过以下代码:

Integer i = new Integer(10);

在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能

  • 自动装箱: 就是将基本数据类型自动转换成对应的包装类。
  • 自动拆箱:就是将包装类自动转换成对应的基本数据类型。
Integer i =10;  //自动装箱
int b= i;     //自动拆箱

Integer i=10 可以替代 Integer i = new Integer(10);,这就是因为Java帮我们提供了自动装箱的功能,不需要开发者手动去new一个Integer对象。
对上面代码反编译javac可以得到如下的结果:

Integer integer=Integer.valueOf(1); 
int i=integer.intValue();

下面是包装类的继承关系:
在这里插入图片描述

int和Integer的区别(使用场景)?

区别:
1.Integer是int的包装类,int则是java的一种基本数据类型
2.Integer变量必须实例化后才能使用,而int变量不需要
3.Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4.Integer的默认值是null,int的默认值是0

带来的问题:
问题1:相同值的比较
经过包装后的对象不能直接使用==来判断值是否相同(谨慎使用,-128~127的范围内是相同的,其他时候不同),一般是通过equals。

Integer i=127;
Integer i2=127;
System.out.println(i==i2);//true
Integer i3=128;
Integer i4=128;
System.out.println(i3==i4);//false
System.out.println(i3.equals(i4));//true

问题2:拆装箱的效率
由于java提供了默认拆装箱的操作,比如Map<Long, Double>,这个时候就自动从long -> Long , double -> Double。
使用EC(不用拆装箱)和原生的java的效率比较如下:
在这里插入图片描述

String是最基本的数据类型吗?

java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类。String类提供了数值不可改变的字符串。而StringBuffer类提供的字符串进行修改。

为什么String类型是不可变的?

  • 安全。String常常用来表示url,sql语句、文件路径。如果String可变,存在安全隐患。
  • String不可变,那么他的Hashcode就一样, 不用每次重新计算了,针对于 Map、Set 等容器,他们的键值需要保证唯一性和一致性。因此,String 的不可变性使其比其他对象更适合当容器的键值
  • 在并发场景下,由于 String 是不可变的,不会引发线程的问题而保证了线程安全,显著简化了在多线程环境中使用String的工作

另外String字符串“abc“在java7以前是存储在方法区常量池里的,在java8后存储在堆中。

String中的“+”是如何实现的?

String c = "xx" + "yy " + a + "zz" + "mm" + b;
实质上的实现过程是:

String c = new StringBuilder("xxyy")
.append(a).append("zz").append("mm").append(b)
.toString();

底层通过 StringBuilder实现。

平常我们定义的String str=”a”;其实和String str=new String(“a”)还是有差异的。

前者默认调用的是String.valueOf来返回String实例对象,至于调用哪个则取决于你的赋值,比如String num=1,调用的是

public static String valueOf(int i) {
return Integer.toString(i);
}

后者则是调用如下部分:

public String(String original) {
this.value = original.value;
this.hash = original.hash;
} 

最后我们的变量都存储在一个char数组中

private final char value[];

为什么使用局部内部类和匿名内部类时,都要求局部变量为final?

我们先来看这样一段代码:

public class Test {
    public static void main(String[] args)  {
         
    }
     
    public void test(final int b) {
        final int a = 10;
        new Thread(){
            public void run() {
                System.out.println(a);
                System.out.println(b);
            };
        }.start();
    }
}

这段代码需要使用final才能编译通过,使用javac这段代码会被编译成两个class文件:Test.class和Test$1.class。
当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。将这段代码的字节码反编译可以得到下面的内容:
在这里插入图片描述
我们看到在run方法中有一条指令:
bipush 10
这条指令表示将操作数10压栈,表示使用的是一个本地局部变量。这个过程是在编译期间由编译器默认进行,如果这个变量的值在编译期间可以确定,则编译器默认会在匿名内部类(局部内部类)的常量池中添加一个内容相等的字面量或直接将相应的字节码嵌入到执行字节码中。这样一来,匿名内部类使用的变量是另一个局部变量,只不过值和方法中局部变量的值相等,因此和方法中的局部变量完全独立开
所以,如果不限制局部变量是final的话,如果中途改变了a的值的话,会造成两个局部变量的值不相同。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值