如何在Java中模拟指针类型操作?Java是按引用传递还是按值传递?

3
我正在使用Java对Mysql数据库进行建模。这是我的个人实验。为了让不同的列可以有不同的字符集,我需要字符串来存储表的字符集,并且也需要为每个列存储其字符集。如果列的字符集字段可以指向表的字符集字段,那将非常有帮助。但是我知道Java没有指针。
你有什么想法,如何将一个对象的字段指向另一个对象的字段,以便这两个字段始终匹配?
4个回答

6
Java有引用,这是指针的优点,但没有进行指针运算的能力。
public class Table {

  // name does not store a String, it stores a reference to a String
  private String name;

  // tableName is not passed in by copy, tableName's reference is passed in.
  public Table(String tableName) {
    // this is not a copy assignment, but a reference assignment
    name = tableName;
  }

}

在 Java 中,始终指向一个字段需要记住几件事情。对象是面向对象编程语言中的基本元素,而不是名称。因此,您无法建立对对象内部名称的引用,因为无法确定您是按其基本类型还是按其超类引用对象。由于相同的名称可以存在于超类和子类中(这可能会隐藏超类类型),因此,在没有了解实际类实例的情况下,无法正确解析字段名称引用。
这不是巧合,而是设计如此。实际上,了解类成员字段的外部知识正是使代码维护如此困难的原因,因为没有“垫片”可以在调用方和数据之间插入代码。通过封装数据(将其放在方法调用后面),为未来的代码维护奠定了基础;因为,可以插入代码来生成返回值,而这些返回值基于可能会变化的内部数据元素。
例如:
public class Table {

  public Column[] columns;

  public String name;

  public Table() {
    name = ...;
    columns = ...;
  }

}

public class CreateTableDDL {

  public String statement(Table table) {
    StringBuilder buffer = new StringBuilder();
    buffer.append("CREATE TABLE ");
    buffer.append(table.name);
    buffer.append(" (");
    for (int i = 0; i < table.columns.length; i++) {
      Column column = table.columns[i];
      ...
    }
    ...
    return buffer.toString();
  }

}

columns 公开为类型为 Column 的数组,这本身不是什么坏事,直到我们决定将其作为 List 的一部分以便于在新的漂亮的 TableEditor 中动态添加或删除 Column
既然我们公开了基础数据元素,现在我们必须搜索整个代码库以查找任何使用该字段的地方,并重新编写所有用法,使其现在使用 List 接口。实际上,我们需要做更多的工作,因为我们还必须搜索可能直接使用 columns 字段的每个外部库,因为未知于我们的多个 JAR 可能已经使用了这个公共类。
此外,我们很快会注意到,大部分与 columns 相关的工作实际上是 Table 的工作,但位于“helpers”和辅助类中,这些工作应该最好由 Table 本身处理。
最后,我们甚至可能会注意到,外部类在没有通知表格的情况下修改了表格的列;因为它们绕过了任何可能向表格发出更改警报的代码,直接获取了数据。
如果我们只是简单地这样做
public class Table {

  private Column[] columns;

  private String name;

  public Table() {
    name = ...;
    columns = ...;
  }

  public Column[] getColumns() {
    Column[] copy = new Column[columns.length];
    for (int i = 0; i < columns.length; i++) {
      copy[i] = columns[i].clone();
    }
    return copy;
  }

}

然后,我们可以轻松地将基础存储转换成一个List,并从该列表构建我们的“向后兼容”列数组。即使我们决定我们之前存在的columns字段现在需要是一个字符串到数据类型的映射表,调用代码也不需要进行更改。
 public class CreateTableDDL {

  public String statement(Table table) {
    StringBuilder buffer = new StringBuilder();
    buffer.append("CREATE TABLE ");
    buffer.append(table.getName());
    buffer.append(" (");
    for (int i = 0; i < table.getColumns().length; i++) {
      Column column = table.getColumn(i);
      ...
    }
    ...
    return buffer.toString();
  }

}

@cyotee:Java 实际上使用指针,它不是按值传递的。testValue = "test02"; 不会改变字符串 test01,而是改变指针 testValue 指向字符串 "test02"。正如 Edwin 所说,Java 引用是没有算术运算的指针。 - Mooing Duck
是的,Java在技术上使用指针将对象与内存中的数据匹配。 - cyotee doge
是的,Java在JVM中使用指针来管理数据。而对象是指向内存位置指针的引用。但由于你不能访问指针,只能访问引用;并且由于JVM被构建为按值传递,所以这并不重要。在按引用传递的语言中,变量赋值会给你两个指向同一内存位置的引用,但在Java中,它会给你两个引用,分别持有两个不同内存位置的指针。至少在逻辑上,JVM可能使用指针的指针来节省内存。但它会断开这些链接以保持按值传递的设计。 - cyotee doge
请注意,这些都没有解决我的问题,即如何在Java中模拟指针操作。 - cyotee doge
好的,你实际上问了两个问题。我回答了第二个问题,简而言之,你所要求的做法“非常糟糕”,虽然你可能会通过巧妙的方式实现它,但这从根本上违反了语言范式。 - Edwin Buck
显示剩余2条评论

3
首先,需要定义一些术语。需要注意的是,传递方式并不是指用于管理数据的底层机制,而是指你可以期望通过操作数据得到什么结果。
所有的数据都存储在内存位置中。这个定义在不同的JVM中可能会有所不同,但对于本次讨论来说并不相关。
指针是一种变量,它存储了某个数据的内存位置,而不是实际数据本身。
引用是一种通用术语,用于描述任何存储某种索引值以引用一些数据的变量。因此,指针是引用的一种类型,在本次讨论中我们只关心这种类型。
现在,在一个按引用传递的语言中,在赋值期间实际的数据不会被修改。数据被保存在内存中,并且将分配一个引用值,该值告诉计算机去获取实际数据的内存位置。当你将一个变量赋值给另一个变量时,你分配了相同的值来告诉计算机数据的内存位置。因此,两个引用都是索引同一个数据的。如果你改变实际数据,那么两个引用都会索引到新更改的数据。一个操作可以更改许多变量,只受计算机能力的限制。以下是一个通用的代码示例:
a = <valueof 1>; //"1" is now stored in memory, and a is a index to "1" in memory.
b = a; //b now indexes the same memory location as a. They both index the "1" in memory.

a = <value of 2>; //"2" now replaces "1" at the indexed memory location.
output a; //In this case would get the index value the language uses.
output b; //You'd get the same index value as a since they index the same memory location.
output <valueof a>; //Now you get "2", because however it's done in the language you have extracted the data in the memory location indexed by a.
output <valueof b>; //Same output as before as b indexes the same memory location as a.

在一种按值传递的语言中,b仍然得到了1,而a现在得到了2。这是因为b没有被赋予与a相同的引用,它被赋予了一个新引用的值。通用代码看起来相同,但会给出不同的结果。
a = <valueof 1>; //"1" is now stored in memory, and a is a index to "1" in memory.
b = a; //b now indexes a new memory location that now also stores "1".

a = <value of 2>; //"2" now replaces "1" at the indexed memory location.
output a; //You get "2" because a pass by value language will be designed to give you value, not the index.
output b; //You get "1" because when b was assigned to match a, a stored "1". But b is independent of a once assignment is complete.

混淆的原因在于Java使用传递引用机制来实现按值传递设计,某些情况下。对于基本类型,Java采用按值传递方式。对于对象,它采用按引用传递方式。如果将一个基本类型包装在一个对象中(通常建议这样做),它将作为按引用传递方式处理。但是,虽然字符串是对象,但它们也作为按值传递方式处理。但是字符串很奇怪。


1

Java变量是通过引用传递的。因此,如果您声明了Object obj,那么obj将始终是当前作用域中指向同一对象的引用。当您将obj传递给一个方法时,您并没有传递对象的副本,而是传递了对它的引用。

此外,Java还为实现Iterable接口的集合提供了迭代器,您可能想要了解这些内容。这些作用类似于指向List或类似列表中特定位置的指针。


不,它们像数组索引一样,而不是实际指针。它们告诉我们列表中元素的逻辑位置。指针会给我指向值的内存位置。因此,虽然对象是定义对象在内存中的数据的引用,但从实际意义上讲,它不是指针。如果将其分配给另一个对象,则该对象的字段值将被复制到新对象中,而不是指向原始对象的指针的引用。因此,如果更改原始对象,则新对象保持不变。 - cyotee doge

-1
你可以创建一个枚举类型,列出所有可能的排序类型,并在Table和Column类中添加一个名为collationType的成员/属性,将相同的枚举成员分配给它们所属的对象。

但是,如果我更改一个值,另一个值会改变吗? - cyotee doge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接