简体   繁体   English

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

[英]How to Simulate Pointer Type Action in Java, or Is Java Pass-By-Reference or Pass-By-Value?

I'm modeling Mysql databases as en exercise in Java. 我正在将Mysql数据库建模为Java中的练习。 Personal experiment. 个人实验。 And I want to store the table collation as a string, since the columns can have different collation then the tables, I need to store that also for each column. 而且我想将表排序规则存储为字符串,因为列可以具有与表不同的排序规则,所以我还需要为每个列存储排序规则。 It would be very helpful if the column's collation field could just point to the table's collation field. 如果列的排序规则字段仅指向表的排序规则字段,将非常有帮助。 But I know that Java doesn't have pointers. 但是我知道Java没有指针。

Do you have an idea on how I can point the field of one object, to he field of another object so the two will always match? 您是否对如何将一个对象的场指向另一个对象的场有一个想法,以便使两者始终匹配?

Java has references, which are the good parts of pointers without the ability to do pointer math. 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;
  }

}

As far as always pointing to a field in Java, you must keep in mind a few things. 至于始终指向Java中的字段,您必须牢记一些注意事项。 Objects are the basic element in an Object-Oriented programming language, not names. 对象是面向对象编程语言中的基本元素,而不是名称。 As such, you cannot build a reference to an object's internal names, as it is never clear if you are referencing the Object by its base type or by a super type. 因此,您无法建立对对象内部名称的引用,因为根据对象的基本类型或超类型来引用该对象永远都不清楚。 Since identical names can exist in both super classes and sub classes (which could then hide the super class type), field name references cannot be correctly resolved without knowledge of the actual class instance they are getting resolved upon. 由于在超类和子类中都可以存在相同的名称(这可能会隐藏超类的类型),因此,如果不知道要解析的实际类实例,就不能正确解析字段名称引用。

This is by design, not by accident. 这是设计使然,并非偶然。 In fact, external knowledge of a class's member fields is exactly what makes code maintenance so difficult, as there is no "shim" where one can insert code between the caller and the data. 实际上,类的成员字段的外部知识正是使代码维护如此困难的原因,因为没有“垫片”可以在调用者和数据之间插入代码。 By encapsulating the data (putting in behind a method call) one sets the stage for future code maintenance; 通过封装数据(放入方法调用之后),可以为以后的代码维护打下基础。 because, one can then insert code to generate the return values based on possibly changing internal data elements. 因为,然后可以插入代码以根据可能更改的内部数据元素生成返回值。

An example 一个例子

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();
  }

}

exposes columns as an array of type Column , which isn't necessarily a bad thing, until we decide we want it to be a List of Column so we can dynamically add or remove Column s in a new nifty TableEditor . columnsColumn类型的数组,这不一定是一件坏事,直到我们决定让它成为Column List ,以便我们可以在新的漂亮TableEditor动态添加或删除Column

Since we exposed the base data element, now we must search through the entire code base to find any use of the field, and rewrite all uses to now use a List interface. 由于我们公开了基本数据元素,因此现在我们必须搜索整个代码库以查找该字段的任何用法,并重写所有用法以现在使用List接口。 Actually, we need to do even more than that because we must also search through every external library that might have used the columns field directly, as multiple JARs unknown to us might have used this public class. 实际上,我们需要做的甚至更多,因为我们还必须搜索可能直接使用了columns字段的每个外部库因为我们未知的多个JAR可能已经使用了此公共类。

In addition, we will quickly notice that most of what we are doing with columns is really the Table 's business, but located in "helpers" and auxillary classes which detract from responsibilities best localized in Table. 此外,我们很快就会注意到,我们对columns所做的大部分工作实际上都是Table的业务,但是它们位于“助手”类和辅助类中,这些类有损于Table中最本地化的职责。

Finally, we might even notice that external classes are modifying the columns of a table without the table's knowledge; 最后,我们甚至可能注意到,外部类正在修改表的列,而表却不知道。 because, they bypass any code that might alert the table to the change by grabbing the data directly. 因为,它们绕过了任何可能通过直接获取数据来警告表更改的代码。

If we had simply done 如果我们只是做了

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;
  }

}

Then we could have easily converted the base storage to a List and just constructed our "backwards compatible" array of columns from the list. 然后,我们可以轻松地将基本存储转换为List然后从列表中构造我们的“向后兼容”列数组。 The calling code now doesn't require a change, even if we decide that our previously existing columns field now needs to be a Map of String to DataType . 现在,即使我们确定以前存在的columns字段现在需要是Map of String to DataTypeMap of String to DataType调用代码也不需要更改。

 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();
  }

}

First some definitions. 首先是一些定义。 And keep in mind that the pass by terminology does not refer to the underlying mechanism used to manage data. 并且请记住,术语传递不涉及用于管理数据的底层机制。 It refers to what result you can expect the manipulation of that data to give you. 它指的是您可以期望对该数据进行操作而获得的结果。

All data is stored in memory locations. 所有数据都存储在存储位置中。 How this is defined varies between JVMs, and is not relevant to the discussion. JVM之间的定义方式不同,因此与讨论无关。

A pointer is a variable that stores the memory location of a data, instead of the actual data. 指针是一个变量,用于存储数据的存储位置,而不是实际数据。

A reference is a generic term for any variable that stores some kind of index value that refers to some data. 引用是任何变量的通用术语,该变量存储某种引用某些数据的索引值。 So a pointer is a type of reference. 因此,指针是引用的一种。 And in this discussion, the only type we care about. 在此讨论中,我们关心的唯一类型。

Now in a pass by reference language, the actual data in memory is not being manipulated during assignment. 现在以引用语言传递,在分配过程中不会操纵内存中的实际数据。 The data is saved in memory. 数据保存在内存中。 And the reference is assigned a value that tells the computer to go to the memory location to get the actual data. 并且给引用分配了一个值,该值告诉计算机前往内存位置以获取实际数据。 When you assign one variable to another, you assign the same value that tells the computer the data's memory location. 将一个变量分配给另一个变量时,将分配一个相同的值,该值告诉计算机数据的存储位置。 So both references index the same data. 因此,两个引用都索引相同的数据。 If you change the actual data, then both references will index the newly changed data. 如果您更改实际数据,则两个引用都将为新更改的数据建立索引。 One manipulation can change a number of variables only limited by the capabilities of the computer. 一种操作可以更改许多变量,这些变量仅受计算机功能的限制。 A generic code example follows: 通用代码示例如下:

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.

In a pass by value language, b would still get you 1, and a would now get you 2. This is because b would not have been assigned the same reference as a. 在按值传递语言中,b仍然会给您1,而a现在会给您2。这是因为b不会被分配与a相同的引用。 It would have been assigned the value as a new reference. 该值将被分配为新参考。 The generic code would look the same, but give you different results. 通用代码看起来相同,但结果却不同。

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.

The confusion comes from the fact that Java uses a pass by reference mechanism to achieve a pass by value design, in some cases. 造成混淆的原因是,在某些情况下,Java使用按引用传递机制来实现按值传递设计。 In the case of primitives Java acts as pass by value. 对于基元,Java充当按值传递。 In the case of Objects, it acts as a pass by reference. 对于对象,它充当引用传递。 And if you wrap a primitive in an object, which is usually recommended, it will act as pas by reference. 而且,如果通常建议将原语包装在对象中,则按引用将其用作pas。 But while Strings are objects, they also act as pass by value. 但是,尽管字符串是对象,但它们也可以作为按值传递。 But Strings are weird. 但是字符串很奇怪。

Java variables are passed by reference. Java变量通过引用传递。 So if you have declared Object obj , then obj will always been a reference to that same object in your current scope. 因此,如果您已声明Object obj ,那么obj将始终是当前作用域中对同一对象的引用。 When you pass obj to a method, you're not passing a copy of the object, but a reference to it. obj传递给方法时,不是传递对象的副本,而是传递对该对象的引用。

Furthermore, Java also has Iterators for collections that implement the Iterable interface, which you may want to look into. 此外,Java还为实现Iterable接口的集合提供了Iterators,您可能需要研究一下。 These act like pointers to specific positions within a List or similar. 它们的作用类似于指向List或类似List特定位置的指针。

您创建一个具有所有可能的排序规则类型的列表的枚举,然后将名为collat​​ionType的成员/属性添加到Table和Column类,然后将相同的Enum成员分配给相同的对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM