当前位置: 首页> 科技> 互联网 > 云南网站开发公司找哪家_百度快照提交入口网址_seo网站的优化流程_网络自动推广软件

云南网站开发公司找哪家_百度快照提交入口网址_seo网站的优化流程_网络自动推广软件

时间:2025/9/13 18:42:32来源:https://blog.csdn.net/2301_80867182/article/details/144327980 浏览次数:0次
云南网站开发公司找哪家_百度快照提交入口网址_seo网站的优化流程_网络自动推广软件

一 包装类

1 包装类概念和用途

(每个基本数据类型都有对应的包装类)

(方便涉及到对象的操作,如Object[ ]、集合等的操作)

基本数据类型的包装类

我们前面学习的八种基本数据类型并不是对象,为了将基本类型数据和对象之间实现互相转化,Java为每一个基本数据类型提供了相应的包装类。
在这里插入图片描述

包装类基本知识

Java是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到Object[ ]数组或集合中的操作等等。

为了解决这个不足,Java在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。

包装类位于java.lang包,八种包装类和基本数据类型的对应关系:
在这里插入图片描述

在这八个类名中,除了IntegerCharacter类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写而已。

在这里插入图片描述

Number类是抽象类,因此它的抽象方法,所有子类都需要提供实现。Number类提供了抽象方法:intValue()longValue()floatValue()doubleValue(),意味着所有的“数字型”包装类都可以互相转型。

下面我们通过一个简单的示例认识一下包装类。

【示例】初识包装类

public class WrapperClassTest {public static void main(String[ ] args) {Integer i = new Integer(10);  //从java9开始被废弃Integer j = Integer.valueOf(50); //官方推荐}
}

示例内存分析如图所示:

在这里插入图片描述

在这里插入图片描述

包装类的用途

对于包装类来说,这些类的用途主要包含两种:

  1. 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[ ]、集合等的操作。
  2. 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)

【示例】包装类的使用

public class Test {/** 测试Integer的用法,其他包装类与Integer类似 */void testInteger() {// 基本类型转化成Integer对象Integer int1 = new Integer(10);  //已废弃,不推荐使用Integer int2 = Integer.valueOf(20); // 官方推荐这种写法// Integer对象转化成intint a = int1.intValue();// 字符串转化成Integer对象Integer int3 = Integer.parseInt("334");Integer int4 = new Integer("999");// Integer对象转化成字符串String str1 = int3.toString();// 一些常见int类型相关的常量System.out.println("int能表示的最大整数:" + Integer.MAX_VALUE); }public static void main(String[ ] args) {Test test = new Test();test.testInteger();}
}

2 自动装箱和拆箱

(自动装箱(autoboxing)和拆箱(unboxing):将基本数据类型和包装类自动转换)

(自动装箱调用的是valueOf()方法,而不是new Integer()方法)

(自动拆箱调用的xxxValue()方法)

(包装类在自动装箱时为了提高效率,对于-128~127之间的值会进行缓存处理。超过范围后,对象之间不能再使用==进行数值的比较,而是使用equals方法)

1 自动装箱:

基本类型的数据处于需要对象的环境中时,会自动转为“对象”。

我们以Integer为例:

Integer i = 5;

编译器会自动转成:Integer i = Integer.valueOf(5),这就是Java的自动装箱。

2 自动拆箱:

每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。

Integer i = Integer.valueOf(5);

int j = i;

编译器会自动转成:int j = i.intValue();

这样的过程就是自动拆箱。

自动装箱/拆箱的本质是:

自动装箱与拆箱的功能是编译器来帮忙,编译器在编译时依据您所编写的语法,决定是否进行装箱或拆箱动作。

【示例】自动装箱

Integer i = 100;//自动装箱
//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);//调用的是valueOf(100),而不是new Integer(100)

【示例】自动拆箱

Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();

自动装箱与拆箱的功能是所谓的“编译器蜜糖(Compiler Sugar)”,虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。如下所示的程序是可以通过编译的:

【示例】包装类空指针异常问题

public class Test1 {public static void main(String[ ] args) {Integer i = null;int j = i;//int j = i.intValue();}
}

3 包装类的缓存问题

整型、char类型所对应的包装类,在自动装箱时,对于**-128~127之间的值会进行缓存处理,其目的是提高效率。**

缓存原理为:如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,如果在则直接获取数组中对应的包装类对象的引用,如果不在该区间,则会通过new调用包装类的构造方法来创建对象。

下面我们以Integer类为例,看一看Java为我们提供的源码,加深对缓存技术的理解,如示例所示。

【示例】Integer类相关源码

public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}
  1. 为Integer类的一个静态内部类,仅供Integer类使用。
  2. 一般情况下IntegerCache.low为-128IntegerCache.high为127

IntegerCache.cache为内部类的一个静态属性,如示例所示。

【示例】IntegerCache类相关源码

private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[ ];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}
}

在这里插入图片描述

由上面的源码我们可以看到,静态代码块的目的就是初始化数组cache的,这个过程会在类加载时完成。

下面我们做一下代码测试,如示例所示。

【示例】包装类的缓存测试

public class Test3 {public static void main(String[ ] args) {Integer in1 = -128;Integer in2 = -128;System.out.println(in1 == in2);//true 因为123在缓存范围内System.out.println(in1.equals(in2));//trueInteger in3 = 1234;Integer in4 = 1234;System.out.println(in3 == in4);//false 因为1234不在缓存范围内System.out.println(in3.equals(in4));//true}
}

3 自定义一个简单的包装类

(仅限于练习,没有实际意义!)

public class MyInteger {private int value;private static MyInteger[] cache = new MyInteger[256];public static final int LOW = -128;public static final int HIGH = 127;//缓存,用静态初始化块,类在加载的时候就执行了static {//[-128,127]for(int i=MyInteger.LOW;i<=HIGH;i++){//-128,0;-127,1;-126,2;cache[i+128] = new MyInteger(i);}}//逻辑public static MyInteger valueOf(int i) {if(i>=LOW&&i<=HIGH) {return cache[i+128];}return new MyInteger(i);}//重写@Overridepublic String toString() {return this.value+"";}//方法public int intValue(){return value;}private MyInteger(int i) {this.value = i;}public static void main(String[] args) {MyInteger m = MyInteger.valueOf(30);System.out.println(m);}
}

二 字符串相关类

(String类代表不可变的字符序列)

(StringBuilder类和StringBuffer类代表可变字符序列)

(可变还是不可变——>都是源码里面有没有final的问题)

(这三个类的用法,在笔试面试以及实际开发中经常用到,必须掌握好)

1 String类

1 String类源码分析

String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为“不可变对象”。 那什么叫做“不可变对象”呢?指的是对象内部的成员变量的值无法再改变。我们打开String类的源码,如图所示:
在这里插入图片描述

我们发现字符串内容全部存储到value[ ]数组中,而变量value是final类型的,也就是常量(即只能被赋值一次)。 这就是“不可变对象”的典型定义方式。

我们发现在前面学习String的某些方法,比如:substring()是对字符串的截取操作,但本质是读取原字符串内容生成了新的字符串。测试代码如下:

【示例】String类的简单使用

public class TestString1 {public static void main(String[ ] args) {String s1 = new String("abcdef");String s2 = s1.substring(2, 4);// 打印:ab199863System.out.println(Integer.toHexString(s1.hashCode()));// 打印:c61, 显然s1和s2不是同一个对象System.out.println(Integer.toHexString(s2.hashCode()));}
}

在这里插入图片描述

在遇到字符串常量之间的拼接时,编译器会做出优化,即在编译期间就会完成字符串的拼接。因此,在使用==进行String对象之间的比较时,我们要特别注意,如示例所示。

【示例】字符串常量拼接时的优化

public class TestString2 {public static void main(String[ ] args) {//编译器做了优化,直接在编译的时候将字符串进行拼接String str1 = "hello" + " java";//相当于str1 = "hello java";String str2 = "hellojava";System.out.println(str1 == str2);//trueString str3 = "hello";String str4 = " java";//编译的时候不知道变量中存储的是什么,所以没办法在编译的时候优化String str5 = str3 + str4;System.out.println(str2 == str5);//false}
}

2 StringBuffer和StringBuilder

(整个过程没有创建新的字符串对象,因为没有final)

(他俩都继承了AbstractStringBuilder类)

(建议用StringBuilder)

StringBufferStringBuilder都是可变的字符序列。

  1. StringBuffer 线程安全,做线程同步检查, 效率较低
  2. StringBuilder 线程不安全,不做线程同步检查,因此效率较高。建议采用该类。
    在这里插入图片描述

在这里插入图片描述在这里插入图片描述

常用方法列表:

  • 重载的public StringBuilder append(…)方法

    可以为该StringBuilder 对象添加字符序列,仍然返回自身对象

  • 方法public StringBuilder delete(int start,int end)

    可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象

  • 方法public StringBuilder deleteCharAt(int index)

    移除此序列指定位置上的 char,仍然返回自身对象。

  • 重载的public StringBuilder insert(…)方法

    可以为该StringBuilder 对象在指定位置插入字符序列,仍然返回自身对象

  • 方法public StringBuilder reverse()

    用于将字符序列逆序,仍然返回自身对象。

  • 方法public String toString()返回此序列中数据的字符串表示形式。

  • 和 String 类含义类似的方法:

    public int indexOf(String str)
    public int indexOf(String str,int fromIndex)
    public String substring(int start)
    public String substring(int start,int end)
    public int length() 
    char charAt(int index)
    

【示例】StringBuffer/StringBuilder基本用法

public class TestStringBufferAndBuilder{public static void main(String[ ] args) {/**StringBuilder*/StringBuilder sb = new StringBuilder();for (int i = 0; i < 7; i++) {sb.append((char) ('a' + i));//追加单个字符}System.out.println(sb.toString());//转换成String输出sb.append(", I can sing my abc!");//追加字符串System.out.println(sb.toString());/**StringBuffer,下面的方法同样适用StringBuilder*/StringBuffer sb2 = new StringBuffer("我爱中国");sb2.insert(0, "爱").insert(0, "我");//插入字符串System.out.println(sb2);sb2.delete(0, 2);//删除子字符串System.out.println(sb2);sb2.deleteCharAt(0).deleteCharAt(0);//删除某个字符System.out.println(sb2.charAt(0));//获取某个字符System.out.println(sb2.reverse());//字符串逆序}
}

3 字符串使用陷阱

String使用的陷阱

String一经初始化后,就不会再改变其内容了。对String字符串的操作实际上是对其副本(原始拷贝)的操作,原来的字符串一点都没有改变。比如:

String s ="a"; 创建了一个字符串

s = s+"b"; 实际上原来的a字符串对象已经丢弃了,现在又产生了另一个字符串s+"b"(也就是ab)。 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的时间和空间性能,甚至会造成服务器的崩溃。

相反,StringBuilder和StringBuffer类是对原字符串本身操作的,可以对字符串进行修改而不产生副本拷贝或者产生少量的副本。因此可以在循环中使用。

【示例】String和StringBuilder在字符串频繁修改时的效率测试

public class Test {public static void main(String[ ] args) {/**使用String进行字符串的拼接*/String str8 = "";long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间long time1 = System.currentTimeMillis();//获取系统的当前时间for (int i = 0; i < 5000; i++) {str8 = str8 + i;//相当于产生了5000个对象}long num2 = Runtime.getRuntime().freeMemory();long time2 = System.currentTimeMillis();System.out.println("String占用内存 : " + (num1 - num2));System.out.println("String占用时间 : " + (time2 - time1));/**使用StringBuilder进行字符串的拼接*/StringBuilder sb1 = new StringBuilder("");long num3 = Runtime.getRuntime().freeMemory();long time3 = System.currentTimeMillis();for (int i = 0; i < 5000; i++) {sb1.append(i);}long num4 = Runtime.getRuntime().freeMemory();long time4 = System.currentTimeMillis();System.out.println("StringBuilder占用内存 : " + (num3 - num4));System.out.println("StringBuilder占用时间 : " + (time4 - time3));}
}

在这里插入图片描述

关键字:云南网站开发公司找哪家_百度快照提交入口网址_seo网站的优化流程_网络自动推广软件

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: