Android 14 已经出来好久好久了…
今天其他的暂且不论,单纯的讲一下 OpenJDK 17 更新的两点变更(扒源代码)~
首先,正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到 java.util.regex.Matcher 类抛出 IllegalArgumentException 的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 DISALLOW_INVALID_GROUP_REFERENCE 标志。
其次,UUID 处理:现在,验证输入参数时,java.util.UUID.fromString() 方法会执行更严格的检查,因此您可能会在反序列化期间看到 IllegalArgumentException。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 ENABLE_STRICT_VALIDATION 标志。
巴拉巴拉,以上是官网的言论,诸君可以看官方的描述地址
原有代码逻辑~
/**
* Creates a {@code UUID} from the string standard representation as
* described in the {@link #toString} method.
*
* @param name
* A string that specifies a {@code UUID}
*
* @return A {@code UUID} with the specified value
*
* @throws IllegalArgumentException
* If name does not conform to the string representation as
* described in {@link #toString}
*
*/
public static UUID fromString(String name) {
String[] components = name.split("-");
if (components.length != 5)
//仅会判断以-分割的数组长度是否等于5来抛出异常
throw new IllegalArgumentException("Invalid UUID string: "+name);
for (int i=0; i<5; i++)
components[i] = "0x"+components[i];
long mostSigBits = Long.decode(components[0]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[1]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[2]).longValue();
long leastSigBits = Long.decode(components[3]).longValue();
leastSigBits <<= 48;
leastSigBits |= Long.decode(components[4]).longValue();
return new UUID(mostSigBits, leastSigBits);
}
新代码逻辑~
/**
* Creates a {@code UUID} from the string standard representation as
* described in the {@link #toString} method.
*
* @param name
* A string that specifies a {@code UUID}
*
* @return A {@code UUID} with the specified value
*
* @throws IllegalArgumentException
* If name does not conform to the string representation as
* described in {@link #toString}
*
*/
public static UUID fromString(String name) {
// BEGIN Android-changed: Java 8 behaviour is more lenient and the new implementation
// might break apps (b/254278943).
// Using old implementation for apps targeting Android older than U.
// 取反! 如果设备的sdk版本,大于等于 UPSIDE_DOWN_CAKE(34)的话,且启用严格验证
if (!(VMRuntime.getSdkVersion() >= VersionCodes.UPSIDE_DOWN_CAKE
&& Compatibility.isChangeEnabled(ENABLE_STRICT_VALIDATION))) {
return fromStringJava8(name);
}
//如果小于或者没开启严格验证,则执行其他的验证
return fromStringCurrentJava(name);
// END Android-changed: Java 8 behaviour is more lenient and the new implementation
// might break apps (b/254278943).
}
如下是java8 的判断方式(跟原来一致)
/**
* Extracted for testing purposes only.
* @hide
*/
public static UUID fromStringJava8(String name) {
String[] components = name.split("-");
if (components.length != 5)
throw new IllegalArgumentException("Invalid UUID string: "+ name);
for (int i=0; i<5; i++)
components[i] = "0x"+components[i];
long mostSigBits = Long.decode(components[0]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[1]).longValue();
mostSigBits <<= 16;
mostSigBits |= Long.decode(components[2]).longValue();
long leastSigBits = Long.decode(components[3]).longValue();
leastSigBits <<= 48;
leastSigBits |= Long.decode(components[4]).longValue();
return new UUID(mostSigBits, leastSigBits);
}
如下是执行34或者开启严格验证的方法:
/**
* Extracted for testing purposes only.
* @hide
*/
public static UUID fromStringCurrentJava(String name) {
if (name.length() == 36) {
char ch1 = name.charAt(8);
char ch2 = name.charAt(13);
char ch3 = name.charAt(18);
char ch4 = name.charAt(23);
if (ch1 == '-' && ch2 == '-' && ch3 == '-' && ch4 == '-') {
long msb1 = parse4Nibbles(name, 0);
long msb2 = parse4Nibbles(name, 4);
long msb3 = parse4Nibbles(name, 9);
long msb4 = parse4Nibbles(name, 14);
long lsb1 = parse4Nibbles(name, 19);
long lsb2 = parse4Nibbles(name, 24);
long lsb3 = parse4Nibbles(name, 28);
long lsb4 = parse4Nibbles(name, 32);
if ((msb1 | msb2 | msb3 | msb4 | lsb1 | lsb2 | lsb3 | lsb4) >= 0) {
return new UUID(
msb1 << 48 | msb2 << 32 | msb3 << 16 | msb4,
lsb1 << 48 | lsb2 << 32 | lsb3 << 16 | lsb4);
}
}
}
return fromString1(name);
}
private static UUID fromString1(String name) {
int len = name.length();
if (len > 36) {
throw new IllegalArgumentException("UUID string too large");
}
int dash1 = name.indexOf('-', 0);
int dash2 = name.indexOf('-', dash1 + 1);
int dash3 = name.indexOf('-', dash2 + 1);
int dash4 = name.indexOf('-', dash3 + 1);
int dash5 = name.indexOf('-', dash4 + 1);
// For any valid input, dash1 through dash4 will be positive and dash5
// negative, but it's enough to check dash4 and dash5:
// - if dash1 is -1, dash4 will be -1
// - if dash1 is positive but dash2 is -1, dash4 will be -1
// - if dash1 and dash2 is positive, dash3 will be -1, dash4 will be
// positive, but so will dash5
if (dash4 < 0 || dash5 >= 0) {
throw new IllegalArgumentException("Invalid UUID string: " + name);
}
long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL;
mostSigBits <<= 16;
mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL;
mostSigBits <<= 16;
mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;
long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;
leastSigBits <<= 48;
leastSigBits |= Long.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL;
return new UUID(mostSigBits, leastSigBits);
}
解析:
从代码的角度看起来是多了好多验证,梳理一下:
1. 判断是否34(sdk版本),且未开启严格验证的情况下,还是按照原有代码执行,仅仅判断 以-分割的数组长度是否等于5,若不等于5,则主动抛出异常;
2. 如果sdk版本大于等于34,且开启了严格验证,则会执行fromStringCurrentJava()& fromString1()方法,
fromStringCurrentJava方法中没有主动抛出异常的代码,判断长度是否=36,不等于则执行 fromString1,若等于则继续执行,获取字符串下标8、13、18、23的值,若这四个值不等于 - ,则执行 fromString1;
在fromString1; 方法中,传递的字符串长度不得大于36,若超出则主动抛异常;
继续判断;对于任何有效输入,dash1 到 dash4 将为正数,dash5 将为正数,负数,但检查 dash4 和 dash5 就足够了:
若是dash4小于0,dash5大于等于0,则主动抛出异常;
原有代码逻辑~
/**
* Attempts to match the entire region against the pattern.
*
* <p> If the match succeeds then more information can be obtained via the
* <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods. </p>
*
* @return <tt>true</tt> if, and only if, the entire region sequence
* matches this matcher's pattern
*/
public boolean matches() {
synchronized (this) {
matchFound = nativeMatcher.matches(groups);
}
return matchFound;
新代码逻辑~
/**
* Attempts to match the entire region against the pattern.
*
* <p> If the match succeeds then more information can be obtained via the
* {@code start}, {@code end}, and {@code group} methods. </p>
*
* @return {@code true} if, and only if, the entire region sequence
* matches this matcher's pattern
*/
public boolean matches() {
synchronized (this) {
matchFound = nativeMatcher.matches(groups);
}
//主要增加一个这个
modCount++;
return matchFound;
}
modCount的描述 :记录该匹配器状态被修改的次数
/**
* Number of times this matcher's state has been modified
*/
int modCount;
不是我说哈,你都是系统代码了,还起个 fromString1 这样的方法名称,丢人不~
就这,有问题私聊我~
再会~