Java 基础:变量 与 字符串
变量
Java中没有初始化的变量是不能直接使用的
局部变量
String msg;
System.out.print(msg);
就会提示错误,我们必须显式的为变量指定一个初值如null。刚开始学Java的时候写出过这样的代码:
Scanner scan = new Scanner(System.in);
String msg;
int x = scan.nextInt();
if (x > 0) {
    msg = "positive";
} else if (x < 0) {
    msg = "negative";
} else if (x == 0) {
    msg = "zero";
}
System.out.print(msg);
乍一看没什么问题,但这个片段放到一般的main方法里是编译不过的。会提示变量msg可能未初始化。虽然我们在if-else语句中已经把可能x取值都考虑完整了,但是编译器不能进行这样一个智能的检测,如果没有最后的else字句,它任务这个if-else语句中的所有字句都可能不会进入。这样的情况下有没有额外的语句对msg进行初始化,那么在最后输出的时候msg可能就是处于未初始化的状态。
类成员变量
类成员变量和局部变量不同,它们可以不进行显式的初始化而直接使用。因为类实例创建时首先会进行一个零值初始化(数值变量都为0值,引用变量都为null,boolean为false)。因而可以像如下这样使用:
class Box {
    public int width;
    public int height;
    public String name;
    public Box() {}
    public Box(String name) {this.name = name}
    public static void main(String[] args) {
        Box b = new Box();
        System.out.println(b.width);
        System.out.println(b.height);
        System.out.println(b.name);
    }
}
以上会输出:
0
0
null
成员变量初始化顺序
初始化顺序以声明顺序为准,构造函数后于(不论是实例还是静态的)初始化块执行。基类初始化先于子类进行。一般来说按照这样的规则分析都可以顺利的推断出成员变量初始化的结果。但是有一些奇葩的情况需要注意,如初始化块中对后面才声明的变量进行赋值操作
public class InitVar {
	{
		x = 3;
	}
	int x = 2;
	public static void main(String[] args) {
		InitVar v = new InitVar();
		System.out.println(v.x);
	}
}
上例中会输出:
2
当我们调换初始化块和声明语句的位置时输出为3,不过需要注意当初始化块中使用的变量比声明要靠前时,只能对其进行赋值操作而不能进行变量值读取操作。如下情况是不允许的:
public class InitVar {
	{
		System.out.println(x);
	}
	int x = 2;
	public static void main(String[] args) {  }
}
将会报
InitVar.java:5: error: illegal forward reference
System.out.println(x);
^
这样约束是为了防止循环初始化即读取b的值来初始化a但是b又在a后才声明。而声明b的地方又用b来初始化a。等同于下面的情况:
public class InitVar {
	int x = y;
	int y = x;
	public static void main(String[] args) {}
}
字符串
字符串初始化可以使用字面常量形式直接初始化,也可以使用new搞出一个对象来
String a = "a word";
String b = new String("b word");
常量池
字符串变量一个很特殊的地方就是它的字面常量一般会进入常量池中。那么它们是什么时候进入常量池的呢?并不是在执行那条语句的时候,而是包含该语句(含有字面字符串常量)的类文件加载的时候就已经加入常量池了。这些常量在Java类文件中有专门的区段进行存储,可以通过命令行javap -v进行查看(编译后的.class文件),其中的Constant Pool一段就是其中包含的常量值。比如如下的Java代码
public class StringConstant {
	public static void main(String[] args) {
		String a = "a word";
		String b = new String("b word");
		System.out.println(a);
		System.out.println(b);
	}
}
编译后执行javap -v StringConstant.class查看其类文件内容截取如下
Constant pool:
   #1 = Methodref          #9.#18         //  java/lang/Object."<init>":()V
   #2 = String             #19            //  a word
   #3 = Class              #20            //  java/lang/String
   #4 = String             #21            //  b word
main方法的字节码如下:
  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String a word
         2: astore_1
         3: new           #3                  // class java/lang/String
         6: dup
         7: ldc           #4                  // String b word
         9: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        12: astore_2
        13: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: aload_1
        17: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        20: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        23: aload_2
        24: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return
ldc表示从常量池中取出常量引用放入操作数栈顶,astore_1表示把当前操作数栈顶的值取出并存入本地变量区域的1号位置中,即完成了String a = "a word"的执行过程。后续的语句是用于new出一个初始的String对象,然后把常量池中的字符串作为构造函数的参数调用构造函数对初始的String对象进行构造。由于invokespecial(用于调用构造函数)和astore_2都要消耗操作数栈上的String对象引用值,因此在new出String对象后调用了dup指令复制了一份在操作数栈上。
由此可见字符常量并非在new时才加入常量池中,而是在类加载时(不过类具有延时加载机制,可能要到第一次真正使用类时才会去解析类文件中的常量并加入常量池)。
String.intern()方法
String 的intern方法可以手工的把程序中通过拼接得到的字符串加入常量池(直接使用常量初始化或者常量定义的话该字面常量就已经存在于常量池中了)。那么String.intern()方法除了炫技之外有什么其他用途呢?应该是用在可能会产生大量重复字符串对象且这些对象还会长期存在的情况下,比如要在内存中记录100w册的图书以供长期查询,然后其中的出版社名称是通过某种方式动态提取生成(比如从其他的RPC接口反序列化得到的),那么虽然有许多出版社名称是一样的对于hashmap之类的使用不造成丝毫影响,但是反序列化时都是动态new出String对象,就造成了资源的浪费(原本可以使用常量池的一个对象,现在有许多重复对象)。此时应该使用字符串的intern方法进行检测,使用一致的字符串对象。
当然一般常量池所在的空间都比较小,如果大量对一些短生命周期的字符串使用intern操作是不明智的。
另外intern方法在1.7中与1.6中过程是不同的,1.7会将调用intern的对象引用放入常量池(如果当前没有),而1.6则会复制一份并将其拷贝的引用放入(参见深入理解JVM)。
类间常量引用
当一个类引用另一个类中的常量时会把常量值之间复制过来,当做一种优化手段这在C++里也是存在的。如果A依赖B,而B修改了常量值,A没有进行更新编译那么它使用的任然是老的。可以通过如下代码进行说明,设有两个文件Box.java和Limits.java,都在默认包下
Limits.java
public class Limits {
	public static final int MAX_WIDTH = 10000;
}
Box.java
public class Box {
	public static void main(String[] args) {
		int width = 1000;
		if (width < Limits.MAX_WIDTH) {
			System.out.println("valid");
		} else {
			System.out.println("invalid");
		}
	}
}
那么当第一次Limits.java、Box.java编译后,如果再仅仅修改Limits.java调整MAX_WIDTH的值然后运行java Box并不能使结果有任何改变。使用javap看Box.class文件即可,其中100就是原来Limits.MAX_WIDTH的值,以直接量的形式包含在了代码中。
 public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: sipush        1000
         3: istore_1
         4: iload_1
         5: bipush        100
         7: if_icmpge     21
...
虽然当跟新一些部署应用时需要要考虑到,有可能仅仅更新依赖jar主程序中的常量并没有改变,必须重新编译主程序。
Java 基础:变量 与 字符串的更多相关文章
- Java基础-变量的定义以及作用域详解
		Java基础-变量的定义以及作用域详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字面量 常量(字面量)表示不能改变的数值(程序中直接出现的值).字面量有时也称为直接量,包 ... 
- Java基础-处理json字符串解析案例
		Java基础-处理json字符串解析案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 作为一名开发人员,想必大家或多或少都有接触到XML文件,XML全称为“extensible ... 
- Java 基础 变量和运算符
		Java基础语法 第1章 变量 1.1 变量概述 1.2 计算机存储单元 1.3 基本类型之4类8种 1.4 常量与类型 1.5 定义变量(创建变量) 1.6 变量使用的注意事项 1.7 数据类型 ... 
- JAVA基础——变量和常量
		JAVA的变量和常量知识总结 一.认识java标识符 标识符就是用于给 Java 程序中变量.类.方法等命名的符号. 使用标识符时,需要遵守几条规则: 1. 标识符可以由字母.数字.下划线(_).美 ... 
- Java基础 - 变量转换
		在java中变量转发分为两种,隐式转换和强制转换 隐式转换: byte a = 10; int b = 20; byte c = a + b; // 该方法会报错,转换过程中字节数只能从小变大,不能从 ... 
- Java基础——变量、数据类型
		一 .变量 1.计算机的内存类似于人的大脑,计算机使用内存来记忆大量运算时要使用数据.内存是一个物理设备,如何来存储一个数据呢?很简单,把内存想象成一间旅馆,要存储的数据就好比要住宿的客人. 首先,旅 ... 
- Java 基础 变量介绍
		变量的声明和使用 概念: 变量是指内存中的一个存储区域,该区域要有自己的名称(变量名).类型(数据类型),该区域的数据可以在同一数据类型的范围内不断变化值: 变量的使用注意事项: Java中的变量必须 ... 
- Java基础-变量常量
		变量 内存中的一小块区域,需要变量名来访问 变量的命名: 变量类型 变量名=变量值 例:String stuName= "wangwei"; java中的所有标点符号都是英文的 变 ... 
- java基础18 String字符串和Object类(以及“equals” 和 “==”的解析)
		一.String字符串 问:笔试题:new String("abc")创建了几个对象?答:两个对象,一个对象是 位于堆内存,一个对象位于字符串常量池 class Demo17 { ... 
- Java基础 变量和数据类型及相关操作
		Java基本语法: 1):Java语言严格区分大小写,好比main和Main是完全不同的概念. 2):一个Java源文件里可以定义多个Java类,但其中最多只能有一个类被定义成public类.若源文件 ... 
随机推荐
- 个人网站搭建时linux中的相关配置记录(mysql,jdk,nginx,redis)
			一.开发计划(包括准备工作,网站大致需求等) 二.服务器(linux/centos)购买.相应环境配置(jdk),软件安装(mysql, nginx, redis).域名解析 三.原型图.代码开发(v ... 
- Python Web框架 tornado 异步原理
			Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理 
- android stdio Error Could not find com.android.tools common 25.2.2
			Error:Could not find com.android.tools:common:25.2.2. Searched in the following locations: file:/D:/ ... 
- 【教程向】——基于hexo+github搭建私人博客
			前言 1.github pages服务生成的全是静态文件,访问速度快: 2.免费方便,不用花一分钱就可以搭建一个自由的个人博客,不需要服务器不需要后台: 3.可以随意绑定自己的域名,不仔细看的话根本看 ... 
- Git笔记:Git介绍和常用命令汇总
			Git 是一个开源的分布式版本控制系统,与 CVS, Subversion 等不同,它采用了分布式版本库的方式,不需要服务器端软件支持. 工作流程 Git 的工作流程大致如下: 克隆 Git 资源作为 ... 
- 浏览器对CSS小数点的解析——坑
			在写移动端项目时,为了将一个元素垂直居中,于是我将元素的高和行高设置成一样的,但是显示出来的结果,却让人不得其解,如下: 可以看到按钮的底部有一条缝隙,一开始以为是代码写错了,于是检查了一下,发现没啥 ... 
- Hibernate主配置文件、映射配置文件以及复合主键查询
			Hibernate.cfg.xml主配置文件 主配置文件中主要配置:数据库连接信息.其他参数.映射信息! 常用配置查看源码: hibernate-distribution-3.6.0.Final\pr ... 
- kafka 生产者基本操作
			kafka自带了一个在终端演示生产者发布消息的脚本--kafka-console-producer.sh 运行该脚本会启动一个进程,在运行该脚本时可以传递相应配置以覆盖默认配置. 参数-- -- pr ... 
- 厌倦了“正在输入…”的客服对话,是时候pick视频客服了
			欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云视频发表于云+社区专栏 关注公众号"腾讯云视频",一键获取 技术干货 | 优惠活动 | 视频方案 什么?! ... 
- Java并发编程-ReentrantLock源码分析
			一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ... 
