[Java基础]1. Java入门
文章目录
一、基本语法
编写 Java
程序时,应注意以下几点:
- 大小写敏感:
Java
是大小写敏感的,这就意味着标识符Hello
与hello
是不同的。 - 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用
public
类的类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为.java
。(如果文件名和类名不相同则会导致编译错误)。 - 主方法入口:所有的
Java
程序由public static void main(String []args)
方法开始执行。 - 包名:包名一般为公司\组织、个人域名反写,全部小写。
二、Java标识符
- 类名、变量名以及方法名都被称为标识符。
- 所有的标识符都应该以字母(A-Z或者a-z)、美元符($)或者下划线(_)开始。
- 首字符之后可以是字母(A-Z或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$age、_value、__age_value
- 非法标识符举例:123abc、-age
三、Java关键字
- 访问控制:private、protected、public
- 类、方法和变量修饰符:abstract、class、extends、final、implements、interface、native、new、static、strictfp、synchronized、transient、volatile
- 程序控制语句:break、case、continue、default、do、else、for、it、instanceof、return、switch、while
- 错误处理:assert、catch、finally、throw、throws、try
- 包相关:import、package
- 基本类型:boolean、byte、char、double、float、int、long、short
- 变量引用:super、this、void
- 保留关键字:goto、const、null
四、Java的三种注释
- 单行注释
//...
- 多行注释
/*......*/
/**......*/
,便于javadoc程序自动生成文档
JavaDoc标记说明:
JavaDoc标记 | 解释 |
---|---|
@author | 指定作者 |
@param | 描述方法的参数 |
@return | 描述方法的返回值 |
@version | 指定版本信息 |
@deprecated | 用来注明被注释的类、变量或方法已经不提倡使用,可能废弃 |
@throws | 描述方法抛出的异常,指明抛出异常的条件 |
@since | 指定最早出现在哪个版本 |
@see | 生成参考其他的JavaDoc文档的连接 |
@link | 同@see,但能够嵌入到注释语句中,为注释语句中的特殊词汇生成连接。 |
注意:
① javadoc针对public类生成注释文档
② javadoc只能在public、protected修饰的方法或者属性之上
③ javadoc注释的格式化:前导*
号和HTML标签
④ javadoc注释要仅写在类、属性、方法之前
示例代码:
package com.gx.demo;
/**
* javaDoc注释
*
* @author hjy
* @version 1.0
*
*/
public class HelloWorld {
/* 多行注释
* 第一个Java程序
* 它将打印字符串 Hello World
*/
public static void main(String []args) {
System.out.println("Hello World"); // 打印 Hello World(单行注释)
}
}
五、Java基本数据类型
Java的两大数据类型:基本数据类型、引用类型
Java语言提供了八种基本数据类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
byte
数据类型是8位(bit)、有符号的、以二进制补码表示的整数,范围是[- 2 7 2^7 27, 2 7 2^7 27-1],默认值为0。short
数据类型是16位、有符号的以二进制补码表示的整数,[- 2 15 2^{15} 215, 2 15 2^{15} 215-1],默认值为0。short数据类型可以节省空间,一个short变量是int型变量所占空间的二分之一。int
数据类型是32位、有符号的以二进制补码表示的整数,[- 2 31 2^{31} 231, 2 31 2^{31} 231-1],一般地整型变量默认为int类型,默认值为0。long
数据类型是 64 位、有符号的以二进制补码表示的整数,[- 2 63 2^{63} 263, 2 63 2^{63} 263-1],默认值是0L
,要在数字末加L
或l
。float
数据类型是单精度、32位、符合IEEE 754标准的浮点数,默认值是0.0f
,在储存大型浮点数组的时候可节省内存空间。double
数据类型是双精度、64 位、符合IEEE 754标准的浮点数,浮点数的默认类型为double类型,默认值是0.0d
。boolean
数据类型表示一位的信息,只有两个取值:true
和false
,默认值是false
。char
类型是一个单一的 16 位 Unicode 字符,[\u0000,\uffff],可以储存任何字符。
java中不能使用float和double来表示货币等精确的值,要准确表示单价等货币类型的数据用DECIMAL或NUMERIC,它们用于保存必须为确切精度的值,例如货币数据
同样的,不要在循环判断条件中检查是否相等时使用浮点值比较,因为浮点数值是不精确的。
Java语言支持一些特殊的转义字符序列:\n
换行(0x0a);\r
回车(0x0d);\f
换页符(0x0c);\b
退格(0x08);\0
空字符(0x20);\s
字符串;\t
制表符;\"
双引号;\'
单引号;\\
反斜杠;\ddd
八进制字符 (ddd);\uxxxx
16进制Unicode字符 (xxxx)。
Java中没有sizeof()
,在C和C++中,需要使用sizeof的最大原因是为了“移植”,因为不同的数据类型在不同的机器上可能有不同的大小;而Java的所有数据类型在所有机器中的大小都是相同的,不需要考虑移植问题。
注意:整形和布尔值之间不能强制转换或隐式转换
基本数据类型的包装类
Java每个基本类型在java.lang
包中都有一个相应的包装类。基本数据类型与包装类的对应关系如下:
Byte | Short | Integer | Long | Float | Double | Character | Boolean |
---|---|---|---|---|---|---|---|
byte | short | int | long | float | double | char | boolean |
八种包装类所继承的父类不全都相同:
① Integer ,Byte,Float,Double,Short,Long都属于Number
类的子类,Number类本身提供了一系列的返回以上六种基本数据类型的操作。
② Character与Boolean属于Object
子类。
包装类的作用:提供一种机制,将基本值“包装”到对象中;提供了一系列实用的方法。
装箱与拆箱
将基本数据类型变成包装类称为装箱。
将包装类的类型变为基本数据类型称为拆箱。
在JDK1.5之后,提供了自动装箱和自动拆箱功能。
在编程中要避免无意中的装箱和拆箱行为,建100万个对象跟100万个整数性能开销是不同的。因为包装类型里面还包含着对象头等信息
基本类型转换为String
① 使用包装类的 toString()
方法
② 使用String类的valueOf()
方法
③ 最简单的是基本类型直接与””
相连接即可;如:34+""
int c = 10;
String str1 = Interger.toString(c);
String str2 = String.valueOf(c);
String str3 = c + "";
String转换成对应的基本类型
①调用包装类的valueOf()
方法转换为基本类型的包装类,会自动拆箱。
②除了Character类之外,其他所有包装类都具有parseXxx
静态方法可以将字符串参数转换为对应的基本类型:
public static byte parseByte(String s)
:将字符串参数转换为对应的byte基本类型。public static short parseShort(String s)
:将字符串参数转换为对应的short基本类型。public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。public static float parseFloat(String s)
:将字符串参数转换为对应的float基本类型。public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。public static boolean parseBoolean(String s)
:将字符串参数转换为对应的boolean基本类型。
public class Demo18WrapperParse {
public static void main(String[] args) {
int num = Integer.parseInt("100");
//int num1 = Integer.valueOf("100");
}
}
如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出
java.lang.NumberFormatException
异常。
基本类型之间的转换
Java中,布尔值是唯一一个不能和其他基本类型之间相互转换的基本类型
自动类型转换(隐式类型转换)
整型、实型、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。转换从低级到高级。
如果比int类型小的类型做运算,java在编译的时候就会将它们统一强转成int类型。
- 在二元表达式中,小的类型自动转化为大的类型
- 字符可以自动提升为整数
- 整数类型可以自动转化为浮点类型,int->float,long->float,long->double都会产生精度损失,其余如int->double都没有精度损失。
float A = 2 + 10f;
float B = A - 11.9f;
System.out.println(B); // 0.10000038
强制类型转换(显式类型转换)
- 强制类型转换可能导致溢出或损失精度
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换
- 浮点数到整数的转换是通过舍弃小数得到(截断),而不是四舍五入,如果想要得到舍入的结果,需要使用
Java.lang.Math
中的round()
方法,但注意round返回类型是long类型,还需要强制转换为int类型才能赋值给int变量。 - Java中不支持C++中的自动强制类型转换,必须由程序显示进行强制类型转换。
六、运算符
算术运算符:+
、-
、*
、/
、%
、+
、+
、--
。
关系运算符:>
、>=
、<
、<=
、==
、!=
。
逻辑运算符:&&
、&
、||
、|
、!
、^
。
赋值运算符:=
、+=
、==
、*=
、/=
、%=
。
三目运算符:variable x = (expression) ? value if true : value if false
instanceof
运算符:(Object reference variable) instanceof (class/interface type)
:判断一个引用类型所引用的对象是否是一个类的实例。
instanceof 左边操作元被显式声明的类型与右边操作元必须是同种类或者有继承关系,即位于继承树的同一个继承分支上,否则编译出错
优先级 | 运算符分类 | 结合顺序 | 运算符 |
---|---|---|---|
高 | 分隔符 | 左结合 | . ,[] ,() ,; ,, |
一元运算符 | 右结合 | ! ,++ ,-- ,- ,~ | |
算术运算符 移位运算符 | 左结合 | * ,/ ,% ,+ ,- ,<< ,>> ,>>> | |
关系运算符 | 左结合 | < ,> ,<= ,>= ,instanceof() ,== ,!= | |
逻辑运算符 | 左结合 | ! ,&& ,|| ,~ ,& ,| ,^ | |
三目运算符 | 右结合 | 布尔表达式 ? 表达式1 : 表达式2 | |
低 | 赋值运算符 | 右结合 | = ,*= ,/= ,%= ,+= ,-= ,<<= ,>>= ,>>>= ,&= ,*= ,|= |
七、数组
数组声明与初始化
初始化就是为数组的数组元素分配内存空间,并为每个数组元素附初始值。
数组完成初始化后,内存空间中针对该数组的各个元素就有个一个默认值:
整数类型(byte、short、int、long)默认值是0;
浮点类型(float、double)默认值是0.0;
字符类型(char)默认值是'\u0000';
布尔类型(boolean)默认值是false;
引用类型(类、数组、接口、String)默认值是null。
数组就是存储数据长度固定的容器,保证多个数据的数据类型一致。数组被初始化后,长度不可变。
-
静态初始化:显式指定每个数组元素的初始值,系统决定数组长度
-
动态初始化:显式指定数组的长度,系统决定每个数组元素的初始值。如果元素是基本数据类型,则初始值为默认值;如果元素是引用类型,则数组的初始值为
null
。//静态初始化 int[] a;//声明,没有初始化 int[] a={1,2,3,4,5}; a = new int[]{1,2,3,4,5}; //动态初始化 int[] price = new int[4]; //二维数组初始化 int[][] arrayA=new int[][]{{1,2},{3,4},{5,6}}; int[][] arrayB=new int[2][2];
注意:不能同时使用静态初始化和动态初始化。即在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值。一旦数组完成初始化,数组在内存中所占的空间将被固定下来,所以数组的长度将不可改变。
int[] a=new int[5]{1,2,3,4,5}; //错误,如果提供了数组初始化操作,则不能定义维表达式
int[] a;
a={1,2,3,4,5}; //错误,数组常量只能在初始化操作中使用
int a[];
a[0]=1; //错误,因为数组没有初始化,不能赋值
int[] arr
和int arr[]
都是可以的
Java语言中,把二维数组看作是数组的数组,数组空间不是连续分配的。所以不要求二维数组每一维的大小相同。
int[][] arrayC=new int[][]{{1},{2,3},{4,5,6},{7,8,9,10}};
int[][] arrayD=new int[3][];//特别注意
arrayD[0]=new int[]{1};
arrayD[1]=new int[]{2,3};
arrayD[2]=new int[]{4,5,6};
arrayD.length;
arrayD[2].length;
arrayD[3].length;//ArrayIndexOutOfBoundsException
二维数组的遍历
for(int row = 0; row < matrix.length; row++){
for (int column = 0; column < matrix[row].length; column++){
...
}
}
Java数组 VS C++数组
Java数组分配在堆上。而C++中,int a[100]分配在栈上,int *a = new int[100]分配在堆上。Java中int a[100];
这个写法是错误的,Java中的[]运算符被预定义为检查数组边界,而且没有指针运算,就不能通过a+1得到下一个数组元素。
对于命令行参数,带有String[] args,在java中,args[0]是第一个参数,程序名没有存储在args中。
数组增强for循环
Java增强型for循环只能取值,不能赋值
for(声明语句 : 表达式)
{
//代码句子
}
数组复制
arrray2 = array1;
:array2指向array1引用的数组。
使用循环来复制数组的每个元素
arraycopy(sourceArray, src_pos, targetArray, tar_pos, length);
或者用Arrays的copyOf函数int[] copiedLuckyNumbers =Arrays.copyOf(luckNumbers,luckyNumbers.length);
第2个参数是新数组的长度。这个方法通常用来增加数组的大小:int[] copiedLuckyNumbers = Arrays.copyOf(luckNumbers,2*luckyNumbers.length);
如果数组元素是数值型,则多余的元素被赋值为0;若是布尔型,则赋值为false。相反,如果长度小于原始数组长度,则只拷贝前面的元素。
数组作为返回值
public static int[] reverse(int[] list){
int[] result = new int[list.length];
...
return result;
}
java.util.Arrays class
Arrays.binarySearch(array, data)
;前提是数组已经排好序,返回data在数组中的位置,没有则返回-1。data的类型可以是int, double, char, chort, long, float
Arrays.sort(array)
,Arrays.parallelSort(array)
.Arrays.toString(array)
命令行参数
class TestMain {
public static void main(String[] args) {
...
}
}
java TestMain arg0 arg1 arg2 ... argn
In the main method, get the arguments from args[0], args[1], …, args[n] , which corresponds to arg0, arg1, …, argn in the command line.
八、Character数据类型
char letter = `A`;(ASCII)
char numChar = `4`;(ASCII)
char letter = '\u0041';(Unicode)
char numChar = '\u0034';(Unicode)
注意:自增自减操作符能用于char型变量上,用于获得下一个或上一个unicode字符,如下面的内容会输出b
char ch = `a`;
Systemo.out.println(++ch);
Java字符集使用unicode(16位编码),每个unicode占据2个字节,前面带有\u
,范围为[\u0000,\uFFFF],可以表示65535+1个字符。ASCII字符集是Unicode的子集,从\u0000到\u007f。
char与int的自动类型转换
int i = 'a'; //same as int i = (int)a
char c = 97; //same as char c = (char)97
Character类的方法
isDigit(ch)
isLetter(ch)
isLetterOrDigit(ch)
isLowerCase(ch)
isUpperCase(ch)
toLowerCase(ch)
toUpperCase(ch)
九、读取控制台输入
nextInt()
方法会读取下一个int型标志的token,但是焦点不会移动到下一行,仍然处在这一行上。next()
从遇到第一个有效字符(非 空格、换行符)开始扫描,遇到第一个分隔符或结束符时结束nextLine()
则是扫描剩下的所有字符串直到遇到回车为止
包装类共同的方法与常用类变量
共同方法:
带有基本值参数并创建包装类对象的构造函数。如利用Integer包装类创建对象,Integer obj=new Integer(145);
带有字符串参数并创建包装类对象的构造函数.如:new Integer(“-45.36”);
可生成对象基本值的typeValue
方法,如:obj.intValue();
将字符串转换为基本值的parseType
方法,如:Integer.parseInt(args[0]);
生成哈希表代码的hashCode
方法,如:obj.hashCode();
对同一个类的两个对象进行比较的equals()
方法,如:obj1.eauqls(obj2);
生成字符串表示法的toString()
方法,如:obj.toString().
Integer类的方法:
Integer.toHexString(500)
:十进制转成十六进制
Integer.toOctalString(500)
:十进制转成八进制
Integer.toBinaryString(500)
:十进制转成二进制
Integer.valueOf("FFFF", 16).toString()
:十六进制转成十进制
Integer.valueOf("776", 8).toString()
:八进制转成十进制
Integer.valueOf("0101", 2).toString()
:二进制转十进制
常用变量
包装类.SIZE
:对应基本数据类型的二进制位数
包装类.MIN_VALUE
:最大值
包装类.MAX_VALUE
:最小值
“==”和“equals”的区别
基本类型 | == | equals |
---|---|---|
字符串变量 | 对象在内存中的首地址 | 字符串内容 |
非字符串变量 | 对象在内存中的首地址 | 对象在内存中的首地址 |
基本类型 | 值 | 不可用 |
包装类 | 地址 | 内容 |
equals方法不能作用于基本数据类型的变量
(一)字符串变量
==
比较两个字符串变量在内存中的地址。equals()
比较字符串中所包含的内容是否相同。
String s1,s2
s1 = new String("abc");
s2 = new String("abc");
System.out.println(s1==s2); //false 两个abc的内存地址不一样,也就是说s1s2指向的对象不一样,故不相等。
System.out.println(s1.equals(s2)); //true 两个变量的所包含的内容是abc,故相等。
注意①:
StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
System.out.println(s1.equals(s2)); //是false
解释:StringBuffer
类中没有重新定义equals
这个方法,因此这个方法就来自Object
类, 而Object
类中的equals
方法是用来比较“地址”的,所以等于false
.
注意②:
String s1 = "abc";
String s2 = "abc";
System.out.println(s1.equals(s2)); //true
System.out.println(s1 == s2); //true
这涉及到了内存中的常量池,常量池属于方法区的一部分,当运行到s1
创建对象时,如果常量池中没有,就在常量池中创建一个对象"abc"
,第二次创建的时候,就直接使用,所以两次创建的对象其实是同一个对象,它们的地址值相等。
(二)非字符串变量
==
和equals
方法的作用是相同的都是用来比较其对象在堆内存的首地址,即用来比较两个引用变量是否指向同一个对象。
class A{
A obj1 = new A();
A obj2 = new A();
System.out.println(obj1==obj2); //false
System.out.println(obj1.equals(obj2)); //false
obj1=obj2;
System.out.println(obj1==obj2); //true
System.out.println( obj1.equals(obj2)); //true
}
注意①:
Integer a=100;
Integer b=1000;
Integer c=100;
Integer d=1000;
System.out.println(a==b); //false
System.out.println(a==c); //true!!!
System.out.println(b==c); //false
System.out.println(b==d); //false
定义一个Integer变量时,会默认进行Integer.valueOf(a)
操作,方法的源码如下:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high) //i的取值为[-128,127]
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
当进行这个方法时如果值在[-128,127],返回的值也就是地址是相同的,所以a和c的地址相同,a==c自然为true
(三)Java语言对equals()的要求
Java语言对equals()
的要求如下,这些要求是必须遵循的:
- 对称性:如果
x.equals(y)
返回是true
,那么y.equals(x)
也应该返回是true
。 - 反射性:
x.equals(x)
必须返回是true
。 - 类推性:如果
x.equals(y)
返回是true
,而且y.equals(z)
返回是true
,那么z.equals(x)
也应该返回是true
。 - 一致性:如果
x.equals(y)
返回是true
,只要x
和y
内容一直不变,不管重复x.equals(y)
多少次,返回都是true
。 - 任何情况下,
x.equals(null)
,永远返回是false
;x.equals
(和x
不同类型的对象)永远返回是false
。
(四)覆盖Object.equals()的方法
在JDK
中有一些类覆盖了Object
类的equals()
方法,它们的比较规则为:如果两个对象的类型一致,并且内容一致,则返回true
。这些类包括:java.io.File
、java.util.Date
、java.lang.String
、包装类(如java.lang.Integer
和java.lang.Double
类)。如果是自己定义的类的话,可以复写equals()
方法,自己定义比较规则。
java.util.Objects类
在JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save
(空指针安全的)或null-tolerant
(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:
public static boolean equals(Object a, Object b)
:判断两个对象是否相等。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
Java集合如何存放基础数据类型
- Java集合如Map、Set、List等所有集合只能存放引用类型数据,它们都是存放引用类型数据的容器,不能存放如int、long、float、double等基础类型的数据。
- Java集合中实际存放的只是对象的引用,每个集合元素都是一个引用变量,实际内容都放在堆内存或者方法区里面,但是基本数据类型是在栈内存上分配空间的,栈上的数据随时就会被收回的。
- 可以通过包装类把基本类型转为对象类型,存放引用就可以解决这个问题。更方便的,由于有了自动拆箱和装箱功能,基本数据类型和其对应对象(包装类)之间的转换变得很方便,想把基本数据类型存入集合中,直接存就可以了,系统会自动将其装箱成封装类,然后加入到集合当中。
int i = 10;
Integer in = new Integer(i);//手动将i包装为Integer类型对象
HashSet set = new HashSet();//定义一个Set集合
set.add(in);//将包装类对象加入该集合
System.out.println(set);//打印结果
//同样的效果(自动包装):
int i = 10;
HashSet set = new HashSet();
set.add(i);//系统会自动将i装箱为Integer类型的对象然后再存入set集合中
System.out.println(set);
数组初始化与内存控制
(一)Java虚拟机的内存划分
区域名称 | 作用 |
---|---|
寄存器 | 给CPU使用 |
本地方法栈 | JVM在使用操作系统功能的时候使用 |
方法区 | 存储可以运行的class文件 |
堆内存 | 存储对象或者数组,new来创建的都存储在堆内存 |
方法栈 | 方法运行时使用的内存,比如main方法运行,进入方法栈中运行 |
(2)数组一定要初始化吗?
int[] nums = new int[]{1, 2, 3, 4, 5};
int[] arr;
arr = nums;
对于数组对象来说,必须初始化,也就是为该数组对象分配一块连续的内存空间,连续内存空间的长度就是数组对象的长度。
对于数组变量来说,不需要进行初始化,只需让其指向一个有效的数组对象就可以。
实际上,所有引用类型的变量,其变量本身不需要任何初始化,需要初始化的是它所引用的对象。
(3)基本数据类型数组的初始化
程序直接先为数组分配内存空间,再将数组元素的值存入对应的内存中。
int[] nums; //①
System.out.println(nums); //②
nums = new int[]{1, 2, 3, 4}; //③
System.out.println(nums.length); //④
①:定义nums数组变量,其位于main方法栈中。
②:不会报错。虽然nums变量并未引用到有效的数组对象,但此时并未通过nums变量访问数组对象的方法或属性。
③:在堆中分配内存,将1,2,3,4,存入对应的内存中。
④:此时访问了nums数组对象的属性,所以要求nums必须引用一个有效的对象。
当通过引用变量来访问实例属性或者调用非静态方法时,如果该引用变量还未指向有效的对象,程序就会抛出运行时异常:NullPointerException,可以通过将第二行代码换为第四行代码来验证。
误区:基本类型的数据的值存储在栈内存中
实际上,应该是所有的局部变量都保存在栈内存中,不管是基本类型还是引用类型,局部变量都保存在各自的方法栈中。
(4)引用类型数组的初始化
引用类型数组的数组元素仍然是引用类型,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象,包括数组和Java对象。
class Person{
String name;
int age;
public void info(){
System.out.println("name:" + name + ", age:" + age);
}
}
//动态初始化一个Person实例
Person[] person = new Person[2]; //①
// 创建两个Person对象
Person p1 = new Person(); //②
p1.name = "张三";
p1.age = 20;
Person p2 = new Person();
p2.name = "李四";
p2.age = 30;
// 将两个Person对象赋给数组元素
person[0] = p1; //③
person[1] = p2;
// 结果一样,p1和person[0]指向同一个对象
p1.info();
person[0].info();
①:在堆中分配出两块连续内存空间,初始化为null;main栈方法中存储引用变量person,指向刚刚分配的内存。
②:同样的在堆中分配内存并赋值,将引用变量p1,p2分别指向新分配的内存
③:person数组的元素是引用类型,故person[0]指向p1指向的内存,person[2]同理。
(5)Java多维数组
所谓多维数组,其实只是数组元素元素依然是数组的数组。 Java允许将多维数组当成1维数组来处理,初始化多维数组时可以先初始化最左边的维数,此时该数组的每个数组元素都相当于一个数组引用变量,这些数组还需要进一步初始化。
public static void main(String[] args) {
int[][] a;
a = new int[4][];
// 把a当成一维数组,遍历a数组的每个数组元素
for(int i=0; i<a.length; i++){
System.out.println(a[i]); // 输出都为null
}
a[0] = new int[2];
a[0][1] = 2;
for(int i=0; i<a[0].length; i++){
System.out.println(a[0][i]); // 输出0和2
}
}
常量池相关
字符串的创建机制:
String content ="111"
:该方式会将值放入字符串常量池Stirng ss = new String("111")
:不放入字符串常量池。
对常量池的优化:当两个对象拥有同样的值时,只是常量池中的一个拷贝。当一个字符串内容经常出现时,可以大幅度节省内存空间。
Java8中对字符串进行了优化,字符串拼接编译的时候会直接编译成StringBuilder
。