这个问题其实还是有必要去谈一谈的,在我们进行字符串赋值的时候一般不会注意到string的长度什么的,因为一般达不到,但是有个特殊的字符串比较长,那就是Base64转码;
base64是进行图片传输的时候特殊的编码方式,这个字符就很长,超过了string的“编译时”接收最大长度。为什么说是编译时呢?带着问题我们走入今天的主题!
首先要知道String的长度限制我们就需要知道String是怎么存储字符串的,String其实是使用的一个char类型的数组来存储字符串中的字符的。
那么String既然是数组存储那数组会有长度的限制吗?是的有限制,但是是在有先提条件下的,我们看看String中返回length的方法
整数在java中是有限制的,我们通过源码来看看int类型对应的包装类Integer可以看到,其长度最大限制为2^31 -1,那么说明了数组的长度是0~231-1,那么计算一下就是(231-1 = 2147483647 = 4GB)
Integer的取值范围看到这我们尝试通过编码来验证一下上述观点。
以上是我通过定义字面量的形式构造的10万个字符的字符串,编译之后虚拟机提示报错,说我们的字符串长度过长,不是说好了可以存21亿个吗?为什么才10万个就报错了呢?
其实这里涉及到了JVM编译规范的限制了,其实JVM在编译时,如果我们将字符串定义成了字面量的形式,编译时JVM是会将其存放在常量池中,这时候JVM对这个常量池存储String类型做出了限制,
至于关于JVM的那块我就不做赘述了,放在文末的原文里了,感兴趣的读者可以看看
问:字符串有长度限制吗?是多少?
答:首先字符串的内容是由一个字符数组 char[] 来存储的,由于数组的长度及索引是整数,且String类中返回字符串长度的方法length() 的返回值也是int ,所以通过查看java源码中的类Integer我们可以看到Integer的最大范围是2^31 -1,由于数组是从0开始的,所以数组的最大长度可以使【0~2^31-1】通过计算是大概4GB。
但是通过翻阅java虚拟机手册对class文件格式的定义以及常量池中对String类型的结构体定义我们可以知道对于索引定义了u2,就是无符号占2个字节,2个字节可以表示的最大范围是2^16 -1 = 65535。其实是65535,但是由于JVM需要有一个结束指令,所以这个范围就为65534了。超出这个范围在编译时期是会报错的,但是运行时拼接或者赋值的话范围是在整形的最大范围。
这里附上一个通过base64进行加密解密的操作:
百度里在线base64图片转码:  throws IOException {
// data:image/jpeg;base64,
int start = imgStr.indexOf("/");
int end = imgStr.indexOf(";");
String ext = "." + imgStr.substring(start + 1, end);
imgStr = imgStr.substring(imgStr.indexOf(",") + 1);
// Base64解码
BASE64Decoder decoder = new BASE64Decoder();
byte[] b = decoder.decodeBuffer(imgStr);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
// 存放位置,文件夹按日期区分
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
SimpleDateFormat fileSdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String time = sdf.format(new Date());
String fileName = fileSdf.format(new Date()) + ext;
File file = new File("C:/file_upload/" + path + time);
if(!file.exists()){
file.mkdirs();
}
OutputStream out = new FileOutputStream(file + "/" + fileName);
out.write(b);
out.flush();
out.close();
String photo_address = path + time + "/" + fileName;
return photo_address;
}
加密操作:
protected String getImageStr(String filePath) {
InputStream inputStream = null;
byte[] data = null;
try {
inputStream = new FileInputStream(filePath);
data = new byte[inputStream.available()];
inputStream.read(data);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
// 加密
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
我之前做的没有上述的那样拆分,直接就是通过substring(22)拿掉了前面的22位(我的是png,注意这个位数,有的是jpeg就是23.)
附上我自己项目里的代码
public static boolean generateImage(String imgStr, String filePath, String fileName) {
try {
if (imgStr == null) {
return false;
}
/*
* Base64 要求把每三个8Bit 的字节转换为四个6Bit 的字节(3*8 = 4*6 = 24 ),然后把6Bit 再添两位高位0 ,
* 组成四个8Bit 的字节,也就是说,转换后的字符串理论上将要比原来的长1/3 。
* */
BASE64Decoder decoder = new BASE64Decoder();
byte[] b = decoder.decodeBuffer(imgStr);
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
OutputStream out = new FileOutputStream(filePath+fileName);
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
return false;
}
}
参考文章:
面试官:String长度有限制吗?是多少?还好我看过
【Java】# Java对图片进行base64编解码