Java的最终版本与C ++的const


151

Java进行C ++程序员教程说,(大亮点是我自己的):

关键字final 大致 等同于C ++中的const

在这种情况下,“大致”是什么意思?他们不是完全一样吗?

有什么区别(如果有)?

Answers:


195

在C ++中,标记成员函数const意味着可以在const实例上调用它。Java没有与此等效的功能。例如:

class Foo {
public:
   void bar();
   void foo() const;
};

void test(const Foo& i) {
   i.foo(); //fine
   i.bar(); //error
}

只能在Java中一次分配值,例如:

public class Foo {
   void bar() {
     final int a;
     a = 10;
   }
}

在Java中合法,但在C ++中不合法,而:

public class Foo {
   void bar() {
     final int a;
     a = 10;
     a = 11; // Not legal, even in Java: a has already been assigned a value.
   }
}

在Java和C ++中,成员变量都可以分别为final/ const。在类的实例完成构造时,需要为它们提供一个值。

在Java中,必须在构造函数完成之前对其进行设置,这可以通过以下两种方法之一来实现:

public class Foo {
   private final int a;
   private final int b = 11;
   public Foo() {
      a = 10;
   }
}

在C ++中,您将需要使用初始化列表为const成员赋予一个值:

class Foo {
   const int a;
public:
   Foo() : a(10) {
      // Assignment here with = would not be legal
   }
};

在Java中,final可用于将事物标记为不可覆盖。C ++(C ++ 11之前的版本)不执行此操作。例如:

public class Bar {
   public final void foo() {
   }
}

public class Error extends Bar {
   // Error in java, can't override
   public void foo() {
   }
}

但是在C ++中:

class Bar {
public:
   virtual void foo() const {
   }
};

class Error: public Bar {
public:
   // Fine in C++
   virtual void foo() const {
   }
};

这很好,因为标记成员函数的语义const是不同的。(您也可以仅通过启用一个成员函数来重载const。(另请注意,C ++ 11允许将成员函数标记为final,请参阅C ++ 11更新部分)


C ++ 11更新:

实际上,C ++ 11确实允许您将类和成员函数都标记为final,具有与Java中相同功能(例如Java)相同的语义:

public class Bar {
   public final void foo() {
   }
}

public class Error extends Bar {
   // Error in java, can't override
   public void foo() {
   }
}

现在可以完全用C ++ 11编写为:

class Bar {
public:
  virtual void foo() final;
};

class Error : public Bar {
public:
  virtual void foo() final;
};

我必须使用G ++ 4.7的预发行版来编译此示例。请注意,const在这种情况下,它不会替代,而是对其进行了增强,提供了与Java最为相似的C ++关键字所看不到的类似于Java的行为。因此,如果您希望成员函数兼而有之final,则const可以执行以下操作:

class Bar {
public:
  virtual void foo() const final;
};

(顺序constfinal位置是必需的)。

以前并没有const成员函数的直接等效项,尽管将函数virtual设为非- 是一个潜在的选择,尽管不会在编译时引起错误。

同样,Java:

public final class Bar {
}

public class Error extends Bar {
}

变成C ++ 11:

class Bar final {
};

class Error : public Bar {
};

(以前,private构造函数可能是您在C ++中最接近的构造函数)

有趣的是,为了保持与C ++ 11以前的代码的向后兼容性,通常final 不是关键字。(以简单,合法的C ++ 98示例为例struct final;,为什么将其设为关键字会破坏代码)


3
您应该使这些方法成为虚拟方法。否则,您真的不会做同一件事
BlueRaja-Danny Pflughoeft

1
在最后一个示例中,您所拥有的是合法的,但值得一提的final int a; a = 10; a = 11;是(这是final作为变量修饰符的目的。)此外,类的最终成员只能在声明时或在构造函数中设置一次。 。
2011年

2
请注意,C ++ 0x final为此目的添加了成员函数装饰器。VC ++ 2005、2008和2010已经使用上下文关键字sealed而不是来实现了final
ildjarn 2011年

@ildjarn-有趣的是,还有另一个我不知道的相对较小的C ++ 0x更改!我可能会在文本中的某处添加一个小注释,以表明这随着C ++ 0x的变化而改变。
柔印

1
显然,人们仍然在使用finalructor的代码库中执行s / const / final / g !
柔印

30

在Java中,final关键字可用于四件事:

  • 在密封它的类或方法上(不允许子类/重写)
  • 在成员变量上声明它可以被设置一次(我想这就是您在说的)
  • 在方法中声明的变量上,以确保可以将其设置一次
  • 在方法参数上声明不能在方法内修改

重要的一点是:Java的final成员变量必须设置一次!例如,在构造函数,字段声明或初始化器中。(但是您不能在方法中设置最终成员变量)。

使成员变量成为final的另一个结果与内存模型有关,如果您在线程环境中工作,这很重要。


您所说的“内存模型”是什么意思?我不明白
托尼2015年

1
@Tony:Java语言规范,第17.4章。内存模型- docs.oracle.com/javase/specs/jls/se8/html/index.html -网上搜寻第一主打
拉尔夫

27

一个const对象只能调用const方法,并且通常被认为是不可变的。

const Person* person = myself;
person = otherPerson; //Valid... unless we declared it const Person* const!
person->setAge(20); //Invalid, assuming setAge isn't a const method (it shouldn't be)

一个final对象不能设置为一个新对象,但是它不是不可变的-没有什么可以阻止某人调用任何set方法。

final Person person = myself;
person = otherPerson; //Invalid
person.setAge(20); //Valid!

Java没有固有的声明对象不变的方法。您需要自己将类设计为不可变的。

当变量是原始类型时,final/的const工作方式相同。

const int a = 10; //C++
final int a = 10; //Java
a = 11; //Invalid in both languages

3
这也是一个很好的答案(就像这里的许多其他答案一样)。不幸的是,我只能接受一个答案。:)
WinWin

1
完美的答案!
ADJ

13

Java final等效于基本值类型上的C ++ const。

对于Java引用类型,final关键字等效于const指针...即

//java
final int finalInt = 5;
final MyObject finalReference = new MyObject();

//C++
const int constInt = 5;
MyObject * const constPointer = new MyObject();

说得好:“最终关键字等于const指针”
ADJ

8

您已经在这里有了一些不错的答案,但是似乎有一点值得补充:constC ++通常用于防止程序的其他部分更改对象的状态。正如已经指出的那样,final在Java中不能做到这一点(除了基元),它只是防止将引用更改为其他对象。但是,如果您使用Collection,则可以使用静态方法防止更改对象

 Collection.unmodifiableCollection( myCollection ) 

这将返回一个Collection对元素进行读取访问的引用,但是如果尝试进行修改,则将引发异常,使其有点像constC ++


8

Java final仅适用于原始类型和引用,而不适用于const关键字可用于任何对象的对象实例本身。

const list<int> melist;final List<Integer> melist;第一个列表进行比较使您无法修改列表,而第二个列表只会阻止您向分配新列表melist


3

除了具有某些微妙的多线程属性外,声明的变量final无需在声明时进行初始化!

即,这在Java中是有效的:

// declare the variable
final int foo;

{
    // do something...

    // and then initialize the variable
    foo = ...;
}

如果使用C ++编写,则无效 const


2

根据维基百科

  • 在C ++中,不仅防止const字段被重新分配,还存在其他限制,即只能在其上调用const方法,并且只能将其作为其他方法的const参数传递。
  • 非静态内部类可以自由访问封闭类的任何字段,无论是否为final。

1
“重新分配”一词不会出现在该页面的当前版本中,也不会类似于您的第二点,这是不正确的还是无关紧要的,具体取决于您对“访问”的含义。“非静态内部”是双向对话。维基百科不是C ++或Java的规范性参考。
罗恩侯爵,

2

我猜它说的是“大约”,因为const当您谈论指针(即,常量指针与指向常量对象的指针)时,C ++中的含义变得复杂。由于Java中没有“显式”指针,因此final不会出现这些问题。


1

让我用switch / case语句示例解释我的理解。

每个case语句中的值必须是与开关值相同的数据类型的编译时常量值。

声明以下内容(在方法中作为本地实例,或在类中作为静态变量(然后向其添加静态),或实例变量。

final String color1 = "Red";

static final String color2 = "Green";

switch (myColor) { // myColor is of data type String
    case color1:
    //do something here with Red
    break;
    case color2:
    //do something with Green
    break;
}

如果color1是类/实例变量而不是局部变量,则此代码将不会编译。如果color1定义为静态最终变量,则将进行编译(然后它将变为静态最终变量)。

当不编译时,会出现以下错误

error: constant string expression required

-7

关键字“ const”表示您的变量保存在ROM(带有微处理器)中。在计算机中,您的变量将保存在RAM区中,用于汇编代码(只读RAM)。这意味着您的变量不在可写RAM中,包括:静态内存,堆栈内存和堆内存。

关键字“ final”表示您的变量保存在可写RAM中,但是您会向编译器注意到,您的变量仅更改了一次。

//in java language you can use:
static final int i =10;
i =11; //error is showed here by compiler

//the same in C++ the same as follows
int i =10;
const int &iFinal = i;

iFinal = 11; //error is showed here by compiler the same as above

我认为“ const”的性能不好,因此Java不使用它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.