是否有可能 "trick" 一个 Java 程序将任何字符串误认为空值?
Is it ever possible to "trick" a Java program into mistaking any string for null?
这是一道面试题:
假设您正在为一个 Android 系统编写代码,该系统利用 JNI 和一些 C++ 代码从串行接口(例如 uart)读取数据。
底层的东西将由 C++ 代码处理,最终数据将被馈送到 Java 接口,在那里它将被视为 String 类型。
问题是:假设 Java 处理读数的代码是这样的:
private void parseSerialData(String input){
if (input==null){
//DO SOMETHING HERE
}
}
是否有可能满足 if 块的条件?
我对 Java 的理解告诉我这永远不可能,因为(我可能完全错了)Java 中的 null 是命名 "there is no reference" 的方式,null 不是' t 一个对象,它不是从 class 实例化的。在 DVM 或 JVM 级别,只要声明了一个变量,就进行了引用,即使堆栈上可能没有为其分配内存。
因此,作为方法参数的局部变量不可能没有引用开始,更不用说后来给它一个引用,它指向一个String对象(即使这个对象可能没有任何信息,它仍然是一个非空对象),因此,永远不会满足 if 条件。
但是我无法确定这道题是不是一道题,我漏掉了什么?特别是考虑到存在 C++,我不知道将 C++ 空引用传递给 Java 是什么感觉,是否有意义?
那么,if 条件会永远满足吗?
是的,当然 input
可以为空。它仅取决于 C++ 代码的作用。这是一个完整的例子。从您的描述中不清楚本机方法是 returns String 还是将其作为参数从本机方法传递给 java 方法,因此我提供了每个示例。
此程序调用 f
五次。前三次只是传递一个常量值。以下两次等待输入(在本机方法中)并根据输入是否为空将 null 或非 null 传递给 f
。
Test.java
public class Test
{
static {
System.loadLibrary("Test");
}
public static void main(String [] args)
{
Test obj = new Test();
obj.main();
}
void main()
{
f(null);
f("not null");
String s1 = null;
f(s1);
String s2 = n1();
f(s2);
n2();
}
public void f(String s)
{
if (s == null)
System.out.println("null");
else
System.out.println(s);
}
native String n1();
native void n2();
}
Test.cc
#include <stdio.h>
#include <jni.h>
#include "Test.h"
static jstring get_string(JNIEnv *env);
JNIEXPORT jstring JNICALL Java_Test_n1(JNIEnv *env, jobject obj)
{
return get_string(env);
}
JNIEXPORT void JNICALL Java_Test_n2(JNIEnv *env, jobject obj)
{
jstring s = get_string(env);
jclass cls = env->GetObjectClass(obj);
jmethodID f = env->GetMethodID(cls, "f", "(Ljava/lang/String;)V");
env->CallVoidMethod(obj, f, s);
}
static jstring get_string(JNIEnv *env)
{
char buf[20];
if (fgets(buf, sizeof buf, stdin) == NULL)
return NULL;
if (buf[0] == '\n')
return NULL;
return env->NewStringUTF(buf);
}
构建说明
$ javac Test.java
$ javah Test
$ gcc --std=c++11 -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -fPIC -shared -o libTest.so Test.cc
$ /usr/bin/java -Djava.library.path=. Test
这是一道面试题:
假设您正在为一个 Android 系统编写代码,该系统利用 JNI 和一些 C++ 代码从串行接口(例如 uart)读取数据。
底层的东西将由 C++ 代码处理,最终数据将被馈送到 Java 接口,在那里它将被视为 String 类型。
问题是:假设 Java 处理读数的代码是这样的:
private void parseSerialData(String input){
if (input==null){
//DO SOMETHING HERE
}
}
是否有可能满足 if 块的条件?
我对 Java 的理解告诉我这永远不可能,因为(我可能完全错了)Java 中的 null 是命名 "there is no reference" 的方式,null 不是' t 一个对象,它不是从 class 实例化的。在 DVM 或 JVM 级别,只要声明了一个变量,就进行了引用,即使堆栈上可能没有为其分配内存。
因此,作为方法参数的局部变量不可能没有引用开始,更不用说后来给它一个引用,它指向一个String对象(即使这个对象可能没有任何信息,它仍然是一个非空对象),因此,永远不会满足 if 条件。
但是我无法确定这道题是不是一道题,我漏掉了什么?特别是考虑到存在 C++,我不知道将 C++ 空引用传递给 Java 是什么感觉,是否有意义?
那么,if 条件会永远满足吗?
是的,当然 input
可以为空。它仅取决于 C++ 代码的作用。这是一个完整的例子。从您的描述中不清楚本机方法是 returns String 还是将其作为参数从本机方法传递给 java 方法,因此我提供了每个示例。
此程序调用 f
五次。前三次只是传递一个常量值。以下两次等待输入(在本机方法中)并根据输入是否为空将 null 或非 null 传递给 f
。
Test.java
public class Test
{
static {
System.loadLibrary("Test");
}
public static void main(String [] args)
{
Test obj = new Test();
obj.main();
}
void main()
{
f(null);
f("not null");
String s1 = null;
f(s1);
String s2 = n1();
f(s2);
n2();
}
public void f(String s)
{
if (s == null)
System.out.println("null");
else
System.out.println(s);
}
native String n1();
native void n2();
}
Test.cc
#include <stdio.h>
#include <jni.h>
#include "Test.h"
static jstring get_string(JNIEnv *env);
JNIEXPORT jstring JNICALL Java_Test_n1(JNIEnv *env, jobject obj)
{
return get_string(env);
}
JNIEXPORT void JNICALL Java_Test_n2(JNIEnv *env, jobject obj)
{
jstring s = get_string(env);
jclass cls = env->GetObjectClass(obj);
jmethodID f = env->GetMethodID(cls, "f", "(Ljava/lang/String;)V");
env->CallVoidMethod(obj, f, s);
}
static jstring get_string(JNIEnv *env)
{
char buf[20];
if (fgets(buf, sizeof buf, stdin) == NULL)
return NULL;
if (buf[0] == '\n')
return NULL;
return env->NewStringUTF(buf);
}
构建说明
$ javac Test.java
$ javah Test
$ gcc --std=c++11 -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -fPIC -shared -o libTest.so Test.cc
$ /usr/bin/java -Djava.library.path=. Test