java-类生命周期(二)

const限定符全解

一、const 修饰普通变量

int const a = 500;

const int a = 600;

上述两种情况相同,都是声明一个const型的变量,它们的含义是:变量a的值不可改变!

二、const 修饰 指针

int b = 500;

const int * a = &b;               //情况1

int const * a = &b;               //情况2

int * const a = &b;               //情况3

const int * const  a = &b;   //情况4

这四种情况,先来分析前三种。第四种是上面三种的一个组合,最后分析。

针对情况1、2、3 其实只是两类情况:在星号左边还是在星号右边。在星号左边则const修饰的是指针所指向的变量,即指针指向为常量;如果是在星号右边,const修饰的是指针本身,即指针本身是常量。下面具体分析一下。

情况1和情况2中const都是位于星号的左侧,情况相同,可以归为一类,都是指针所指向的内容为常量(与const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作。

针对情况1和情况2的变量声明,我们可以看如下情况:

(1) *a = 600;//错误,指针所指内容为常量!

(2) b = 700;    //此时*a 的值就是700了

(3) int c = 800;  a = &c;    //此时*a变成了800了

从上面(1)、(2)、(3)三种情况来看,情况1、情况2这种变量声明方式的意思在于:不能通过a指针来修改a指针所指地址中存放的内容,但是可以通过修改指针所指向的地址。

如图所示,声明变量时a指向0x00000000,其值是500。我们不同通过操作*a来改变0x00000000里面所存的值。但是我们可以改变a指针所指向的地址,比如将a指向0x00000004或者其它地址。也可以新定义一个变量来改变0x00000000里面的值,但是就是不能通过a指针来改变里面存的值。

在情况1、情况2下const int *a;可以在声明的时候不初始化

再来看一下情况3,情况3是指针为常量,也就是说a只能指向0x00000000这个地址。

*a = 600; 在情况3是正确的,它可以通过*a来操控这个地址中所存内容,但是不能改变a指针所指向的地址。

情况3其实还有一种写法:const (int *) a = &b; 因为在声明变量后无法再修改a指针所指向的地址,因此必须在声明的时候初始化

现在再来看看情况4,情况四星号左右两边都有const,也就是说它即是常指针(指针所指向的地址不能变更),其指向的内容也不能通过该指针进行修改。

两个判断对错题:

const int *a = new int;

int * b = a;

错误,因为声明指针的目的是为了对其指向的内容进行改变,而声明的指针e指向的是一个常量,所以不正确;

int * const a = new int;

int *b = a;

正确,因为声明指针所指向的内容可变;

三、const 修饰函数

(1)参数为指针

void function (const int a);               //情况1

void function (const int * a);            //情况2

void function (int const * a);            //情况3

void function (int * const a);            //情况4

情况1:传递过来的参数a在函数中不能被修改(无意义,因为本身就是形参,改不改都不会影响实参)

情况2与情况3相同:a指针所指的内容*a 不能修改

情况4:指针a为常量,其地址不能改动,但是*a可以修改(无意义,改不改a指针指向的地址都不影响实参)

(2)参数为引用

void function (const class & a);   //情况1

void function (const int & a);       //情况2

情况1:在函数内不能改变类对象a,a的成员变量的值也不能被修改

情况2:function()函数不能修改a,对a是只读的

这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效。另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性,且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙。

(3)const 修饰函数返回值

const 修饰函数返回值其实用的并不是很多,他的含义和const修饰普通变量以及指针含义基本相同。

const int function();         //情况1

const int * function();      //情况2

int * const function();      //情况3

情况1:毫无意义,参数返回本身就是赋值,赋值加个const对被赋值的变量无影响。

情况2:调用时 const int * pvalue = function(); 我们可以将function()看成一个变量,那么就是我们前面说的const修饰指针的情况了。此时指针的内容是不能被该指针修改的。

情况3:调用时 int * const pvalue = function(); 同样可以将function()看成一个变量,那么也就是const修饰指针的情况了。

一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。

通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况,原因如下:如果返回值是某个对象并且为const(const A test = A 实例)或返回某个对象的引用为const(const A& test = A实例),则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

四、类中const的使用

(1) const修饰成员函数、成员变量

class point{

private:

int x;

int y;

const int c;                 // 成员常量不能被修改

public:

point(int a=0):c(a) { };     //成员常量在初始化列表中赋值

int get_x() const;        //类内声明

};

int point :: get_value() const   //类外定义,注意const不能省!

{

return x;

}

const 成员函数可以访问类中的所有成员变量(const或非const成员变量),但是都不能修改任何一成成员;

const 成员函数只能调用类中的const成员函数,而不能调用类中的非const成员函数;

如果在非const成员函数中,this指针指示一个类类型的;

如果在const成员函数中,this指针式一个const类类型的;

如果在volatile成员函数中,this指针就是一个volatile类类型的。

数据成员 非const成员函数 const成员函数
非const的数据成员 可以访问,也可以修改值 允许访问,但不能修改值
const数据成员 可以访问,也可以修改值 允许访问,但不能修改值
const对象的数据成员 不允许访问 允许访问,但不能修改值

常数据成员是不能被赋值的!初始化类内部的常量的两种方法

一种方法就是static和const并用,在外部初始化:

class A

{

public:

A(){}

private:

static const int i;

};

const int A :: i =3;

另一种很常见的方法就是初始化列表:

class A

{

public:

A (int i =0): test (i) { }

private:

const int test;

};

(2)指向对象的常指针

Point a,b;

Point * const p = &a;

P = &b;  //错误,p指针声明并赋值后便不能再修改其指向的地址了

(3)指向常对象的指针变量

Point a;

Point const * p;

p = &a;

p指针指向的内容*p不能被p指针修改,p->x= 19;类似的语句在这种情况下就是错误的。

对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。

(4) const修饰类对象/对象指针/对象引用

·  const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。

·  const修饰的对象,只能调用该对象的const成员函数,该对象的任何非const成员函数都不能被调用(除了由系统自动调用的隐式构造函数和析构函数),因为任何非const成员函数会有修改成员变量的企图。

示例:

class AAA

{

void func1( );

void func2( ) const;

}

const AAA aObj;

aObj.func1( ); 错误

aObj.func2( ); 正确

const AAA* aObj = new AAA();

aObj-> func1( ); 错误

aObj-> func2( ); 正确

一道思考题:

以下定义的赋值操作符重载函数可以吗?

class A

{

const A& operator=(const A& a);  //赋值函数

}

A a,b,c:

(a=b)=c;

a = b= c;

(a=b)=c;  错误,在const A::operator=(const A& a)中,参数列表中的const的用法正确,而当这样连续赋值的时侯,问题就出现了:因为a.operator=(b)的返回值是对a的const引用,不能再将c赋值给const常量。

a = b= c;  正确!

五、const常量与define宏定义的区别

Point a;

(1)编译器处理方式不同

define宏是在预处理阶段展开

const常量是在编译运行阶段使用

(2)类型和安全检查不同

define宏没有类型,不做任何类型检查,仅仅是展开

const常量有具体的类型,在编译阶段会执行类型检查

(3)存储方式不同

define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存

const常量会在内存中分配(可以是堆中,也可以是栈中)

六、将const类型转换为非const类型的方法

采用const_cast 进行转换。

用法:const_cast <type_id>  (expression)

该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。

·  常量指针被转化成非常量指针,并且仍然指向原来的对象;

·  常量引用被转换成非常量引用,并且仍然指向原来的对象;

·  常量对象被转换成非常量对象。

示例:

#include<iostream>

using namespace std;

const int * find( int val, const int *t, int n);

int main()

{

int a[ ] = {2, 4, 6};

int * ptr;

ptr = const_cast<int *>(find(4, a, 3));

if(ptr == 0)

cout<< "not found\n";

else

cout<< "found; value = " << *ptr << ‘\n‘;

return 0;

}

const int * find( int val, const int * t, int n)

{

int i;

for( i=0; i<n; i++)

if( t[i] == val)

return &t[i];

return 0;  // not found

}

七、使用const的一些建议

1、要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;

2、要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;

3、在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;

4、const在成员函数中的三种用法要很好的使用;

5、不要轻易的将函数的返回值类型定为const;

6、除了重载操作符外一般不要将返回值类型定为对某个对象的const引用。

java-类生命周期(二),布布扣,bubuko.com

时间: 2024-10-13 00:00:12

java-类生命周期(二)的相关文章

java类生命周期详细解析

(一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你“怎样做”,但至于“为什么这样做”却不多说,所以造成大家在基础和原理方面的知识比较匮乏,所以笔者今天就斗胆来讲一下这个问题,权当抛砖引玉,希望对在这个问题上有疑惑的朋友有所帮助,文中有说的不对的地方,也希望各路高手前来指正. 首先来了解一下jvm(java虚拟机)

(转)JVM类生命周期概述:加载时机与加载过程

原文地址: http://blog.csdn.net/justloveyou_/article/details/72466105 JVM类加载机制主要包括两个问题:类加载的时机与步骤 和 类加载的方式.本文主要阐述了第一个问题,关于类加载的方式等方面的内容,包括JVM预定义的类加载器.双亲委派模型等知识点, 一个Java对象的创建过程往往包括两个阶段:类初始化阶段 和 类实例化阶段. 注意,本文内容是以HotSpot虚拟机为基准的. 一.类加载机制概述 我们知道,一个.java文件在编译后会形成

JVM类生命周期概述:加载时机与加载过程

一个.java文件在编译后会形成相应的一个或多个Class文件,这些Class文件中描述了类的各种信息,并且它们最终都需要被加载到虚拟机中才能被运行和使用.事实上,虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型的过程就是虚拟机的类加载机制.本文概述了JVM加载类的时机和生命周期,并结合典型案例重点介绍了类的初始化过程,进而了解JVM类加载机制. 一.类加载机制概述 我们知道,一个.java文件在编译后会形成相应的一个

hibernate session缓存和java对象生命周期

一.java对象生命周期 1.在java中,使用new关键字,创建一个java对象,jvm就为这个对象分配一块内存空间.只要这个变量被引用,他就一直存在于内存中.如果没有被任何变量引用(包括间接引用),那么这个对象就会被垃圾回收器回收.下面用一段代码来解释: Customer c=new Customer(); Order o1=new Order(); Order 02=new Order(); o1.setCustomer(c); c.getOrders().add(o1); o1=null

Java学习之==&gt;Java线程生命周期与状态切换

一.Java线程生命周期与状态切换 这些状态的描述可以总结成下图: NEW 一个刚创建但尚未启动的Java线程实例就是处于 NEW 状态 public class App { public static void main(String[] args) { Thread thread = new Thread(); Thread.State state = thread.getState(); System.out.println(state); } } // 输出结果 NEW RUNNABLE

java 静态变量生命周期(类生命周期)

Static: 加载:java虚拟机在加载类的过程中为静态变量分配内存. 类变量:static变量在内存中只有一个,存放在方法区,属于类变量,被所有实例所共享 销毁:类被卸载时,静态变量被销毁,并释放内存空间.static变量的生命周期取决于类的生命周期 类初始化顺序: 静态变量.静态代码块初始化 构造函数 自定义构造函数 结论:想要用static存一个变量,使得下次程序运行时还能使用上次的值是不可行的.因为静态变量生命周期虽然长(就是类的生命周期),但是当程序执行完,也就是该类的所有对象都已经

(转)《深入理解java虚拟机》学习笔记7——Java虚拟机类生命周期

C/C++等纯编译语言从源码到最终执行一般要经历:编译.连接和运行三个阶段,连接是在编译期间完成,而java在编译期间仅仅是将源码编译为Java虚拟机可以识别的字节码Class类文件,Java虚拟机对中Class类文件的加载.连接都在运行时执行,虽然类加载和连接会占用程序的执行时间增加性能开销,但是却可以为java语言带来高度灵活性和扩展性,java的针对接口编程和类加载器机制实现的OSGi以及热部署等就是利用了运行时类加载和连接的特性,java的Class类在虚拟机中的生命周期如下: 上图中加

Java WebSocket生命周期

本章将讲述WebSocket端点的生命周期.WebSocket端点的生命周期为开发人员提供了一个框架来管理端点所需要的资源,也提供了一个框架来拦截消息.我们将仔细探讨其生命周期的顺序和语义,以及Java WebSocket API如何提供API和注解来支持处理这些事件. 一.WebSocket协议 与基于HTTP的技术不同,WebSocket具有生命周期.此生命周期周期由WebSocket协议支撑.WebSocket协议定义了客户端和服务器长时间存活的专用的TCP连接,一旦连接已经建立,数据的传

Java 线程生命周期

2.线程的生命周期 与人有生老病死一样,线程也同样要经历开始(等待).运行.挂起和停止四种不同的状态.这四种状态都可以通过Thread类中的方法进行控制.下面给出了Thread类中和这四种状态相关的方法. // 开始线程 publicvoid start( ); publicvoid run( ); // 挂起和唤醒线程 publicvoid resume( );     // 不建议使用 publicvoid suspend( );    // 不建议使用 publicstaticvoid s

Java实现生命周期管理机制

先扯再说 最近一直在研究某个国产开源的MySQL数据库中间件,拉下其最新版的代码到eclipse后,启动起来,然后做各种测试和代码追踪:用完想要关闭它时,拉出它的STOP类想要运行时,发现这个类里赫然只写以下几行代码,于是我感觉瞬间受到了很多伤害. public static void main(String[] args) { System.out.println(new Date() + ",server shutdown!"); } 这个中间件启动和运行的时候,开启了监听,启动着