最近事情比较多,所以很久没有更新博客。今天把断断续续看书总结的一些知识点给大家说说。并且奉上在此期间自己做的一些资料。在看书的过程中我使用xmind做了一个思维导图,大致能够涵盖第二章里面的知识点。直接上图:
想要获取xmind源文件的朋友可关注公众号【码小跳】回复“一切都是对象” 获取。
就按照思维导图进行展开:
1. 用句柄操纵对象
这里的句柄通俗一点讲其实就是我们在编写程序过程中声明的一个又一个的存放在栈内的变量。而对象则是通过new关键字声明的一个又一个的存放在堆内存中的实例。
遥控器对应句柄,电视对应对象。握住了遥控器那么就相当于掌握了与电视的链接通道。一旦需要“换频道”或者“关小声音”,我们实际操作的是遥控器,再由遥控器(句柄),操纵电视机(对象)。
此外即使没有电视机,遥控器也可以存在。也就是说可以声明一个变量但是不进行初始化。
String str = new String("151515");
如上所示,str是句柄,而等式右边的new String(“151515”)则会在内存中生成一个对象。我们操纵的是str,通过str这个句柄就可以实现对对象的控制。
2. 所有的对象都必须创建
存放位置
在内存中有6个地方是可以将程序运行过程中产生的临时数据进行存储的。
序号 |
位置 |
说明 |
1 |
寄存器 |
最快的保存区域,由编译器进行分配,找不到任何踪迹 |
2 |
堆栈 |
句柄、基本数据类型等存放的位置,通过堆栈指针控制内存的释放和占用 |
3 |
堆 |
对象存放的位置 |
4 |
静态存储 |
使用static关键字进行声明的静态变量会被置入RAM的固定位置进行存储,方便随时调用 |
5 |
常数存储 |
常数值通常直接置于程序代码内部 |
6 |
非RAM存储 |
?? |
package d20180821;
/**
* 用句柄操纵对象
*
* @author Administrator
*
*/
public class 用句柄操纵对象 {
/**常数存储*/
public static final int hh = 9;
/**静态存储*/
public static int uu;
public static void main(String[] args) {
/**等式左边的str句柄存放在栈内,
* 等式右边的new String("151515")这个对象则存储在堆内存中*/
String str = new String("151515");
/**等式左边的sc句柄存放在栈内,
* 等式右边的new SimpleClass()这个对象则存储在堆内存中*/
SimpleClass sc = new SimpleClass();
}
}
class SimpleClass{}
特殊情况:主要类型
在java设计中,由于用 new 创建对象(特别是小的、简单的变量)并不是非常有效(new将对象都置于堆中),这对于程序的设计是不利的。因此java设计者设计了基本数据类型,基本数据类型并非句柄,而是容纳了具体数值的变量。存放在堆栈内,方便快速读取和写入。
Java设计者提供了8种基本数据类型。
主类型 |
大小 |
最小值 |
最大值 |
封装器类型 |
boolean |
1位 |
- |
- |
Boolean |
char |
16位 |
0 |
2的16次方 - 1 |
Character |
byte |
8位 |
-128 |
127 |
Byte |
short |
16位 |
-2的15次方 |
2的15次方 - 1 |
Short |
int |
32位 |
-2的31次方 |
2的31次方 - 1 |
Integer |
long |
64位 |
-2的63次方 |
2的63次方 - 1 |
Long |
float |
32位 |
-2的31次方 |
2的31次方-1 |
Float |
double |
64位 |
-2的63次方 |
2的63次方-1 |
Double |
为了能够直观地查看每种基本数据类型的大小,最小值和最大值以及引入针对基本数据类型的操作的方法。Java设计者给每一个基本类型设计了对应的封装器(包装类型)。
为了解决以上基本数据类型在记录数据时不同程度精度丢失的问题。Java设计者还引入了另外两种高精度数据类型BigDecimal 和BigInteger。
针对8种数据类型,为了加深对8种数据类型的理解。结合代码进行分析如下:
package d20180821;
/**
* 基本类型的实例
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}
基本数据类型的运算是相对复杂内容,在后续的更新中再做介绍。
针对高精度数字类型BigDecimal和BigDecimal, 声明方法如下:
package d20180821;
import java.math.BigDecimal;
/**
* int类型的练习
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
BigDecimal bignum1 = new BigDecimal("2.68");
BigDecimal bignum2 = new BigDecimal("5.10");
BigInteger bignum1 = new BigInteger("2"); // 不能为浮点数
BigInteger bignum2 = new BigInteger("5"); // 不能为浮点数
// BigDecimal的构造器方法可以将一个数字字符串转成一个数字
//BigDecimal bignum1 = new BigDecimal(2.68); // 如果是数字类型, 必须是浮点数
//BigDecimal bignum2 = new BigDecimal(5.10); // 如果是数字类型, 必须是浮点数
BigDecimal bignum3 = null;
//加法
bignum3 = bignum1.add(bignum2);
System.out.println("和 是:" + bignum3);
//减法
bignum3 = bignum1.subtract(bignum2);
System.out.println("差 是:" + bignum3);
//乘法
bignum3 = bignum1.multiply(bignum2);
System.out.println("积 是:" + bignum3);
//除法
bignum3 = bignum1.divide(bignum2);
System.out.println("商 是:" + bignum3);
}
}
java数组
在程序设计语言中C++在开发过程中应尽量避免使用数组,因为内存层面的错误可能让程序出现错误。而java因为在程序设计之初就会对数组成员进行初始化, 保证每个数组成员都有效。虽然因为需要少量开销用于在程序运行期间的索引工作,但是却换来了数组使用的较高安全性。运行如下代码:
package d20180821;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 数组初始化练习
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
int intV;
System.out.println(intV);
int[] intArr = new int[5];
System.out.println(intArr[0]);
}
}
我们发现System.out.println(intV); 根本就不能通过编译。
3.绝对不要清除对象
本小结主要阐述了Java中的作用域的问题。基本数据类型和对象数据类型的作用域是完全不同的。在同一方法体内,花括弧可以对基本数据类型和对象数据类型的作用域分别进行隔断。 但是所不同的是,当程序运行到 int b = 9 后面的花括号后时,变量b消失,而test0所指向的Test()对象仍然存在于内存堆中等待GC对其进行回收。我们没有办法访问该对象,因为指向它的唯一一个句柄已经超出了作用域的边界。
package d20180821;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 作用域类型的练习
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
int i = 0;
Test test = new Test();
{
Test test0 = new Test();
int b = 9;
}
System.out.println(i);
System.out.print(test);
System.out.println(b);
System.out.print(test0);
}
}
class Test {
}
在方法体之间进行传递的过程中基本数据类型和对象数据类型表现出来的差异就很明显了。
package d20180821;
/**
* 作用域类型的练习
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
int baseInt = 0;
System.out.println("在调用方法之前:" + baseInt);
changeBase(baseInt);
System.out.println("在调用方法之后:" + baseInt);
Test test = new Test();
System.out.println("在调用方法之前:" + test.a);
changeObject(test);
System.out.println("在调用方法之后:" + test.a);
}
/**
* 基本数据类型测试方法
*
* @return
*/
public static int changeBase(int i) {
i = 9;
return i;
}
/**
* 对象类型测试方法
* @return
*/
public static Test changeObject(Test t) {
t.a = 5;
return t;
}
}
class Test {
int a ;
public Test() {
this.a = 0;
}
}
从以上这个例子中,可以看出基本数据类型和对象数据类型在作用域上的不同。基本数据类型在程序运行到作用域之外后就失效,而对象数据类型只要还存在句柄指向它那么就会一直存在于堆内存中,针对该对象实例的更改都会被记录在该对象内。
4.新建数据类型:类
我们在 Java 里的全部工作就是定义类、制作那些类的对象以及将消息发给那些对象。而为了完成这个工作,Java设计者设定可在Java类中设置两种类型的元素:数据成员(字段)以及成员函数(方法)。其中字段可以是对象类型也可以是基本数据类型,在构建器实例化一个对象的时候,不管是对象类型还是基本数据类型如果不人为赋值,那么都会自行给定默认值。
主类型 |
默认值 |
boolean |
false |
char |
‘\u0000’(null) |
byte |
(byte)0 |
short |
(short)0 |
int |
0 |
long |
0L |
float |
0.0f |
double |
0.0d |
对象类型 |
null |
运行以下代码查看结果:
package d20180821;
/**
* 类默认值
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
Test test = new Test();
System.out.println("intField:" + test.intField);
System.out.println("booleanField:" + test.booleanField);
System.out.println("charField:" + test.charField);
System.out.println("longField:" + test.longField);
System.out.println("doubleField:" + test.doubleField);
System.out.println("floatField:" + test.floatField);
System.out.println("shortField:" + test.shortField);
System.out.println("byteField:" + test.byteField);
System.out.println("type:" + test.type);
}
}
class Test {
int intField ;
boolean booleanField;
char charField;
long longField;
double doubleField;
float floatField;
short shortField;
byte byteField;
OtherType type;
}
class OtherType{
}
5.方法、自变量和返回值
函数在java程序中也叫做“方法”,通常由名字、自变量、返回类型以及主体这四部分组成。格式如下:
返回类型 方法名( /* 自变量列表 */ ) {/* 方法主体 */} 通常情况下会在开头位置用访问权限修饰符 public、private、protected等进行修饰规定方法可以被调用的范围。
返回类型可以为基本类型、对象类型,当然也可以没有返回类型这时用void表示。
package d20180821;
/**
* 方法定义
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
}
/**
* 一个没有返回值、没有自变量的方法
*/
public void aMethod() {
int i = 90;
int a = 89;
System.out.print(i*a);
}
/**
* 返回值为int、没有自变量的方法
* @return
*/
public int aMethod0() {
int i = 90;
int a = 89;
return (i*a);
}
/**
* 返回值为long、没有自变量的方法
* @return
*/
public long aMethod1() {
int i = 90;
int a = 89;
return i*a;
}
/**
* 返回值为对象类型、没有自变量的方法
* @return
*/
public Test aMethod2() {
Test test = new Test();
return test;
}
/**
* 返回值为int 、有两个int值的自变量列表
*
* @param a
* @param b
* @return
*/
public int aMethod3(int a, int b) {
return a*b;
}
/**
* 返回值为Test对象、自变量为Test对象
* @param test
* @return
*/
public Test aMethod4(Test test) {
test.i = 9;
return test;
}
}
class Test{
int i;
}
6.构建Java程序
名字的可见性
Java为了能够克服程序设计中名字重复的问题,引入了“命名空间”这个概念。Java 的设计者鼓励程序员反转使用自己的Internet 域名,因为它们肯定是独一 无二的。Java 的这种特殊机制意味着所有文件都自动存在于自己的命名空间里。而且一个文件里的每个类都自动获得 一个独一无二的标识符(当然,一个文件里的类名必须是唯一的)。所以不必学习特殊的语言知识来解决这 个问题——语言本身已帮我们照顾到这一点。
使用其他组件
为了使用Java类库中的其他API, 我们需要使用"import"关键字来引入其他的类。示例如下:
package Gys;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.*;
* 号表示引入该目录下所有的文件。
static关键字
通常情况下,只有在new一个对象之后,我们才可以使用对象中创建的方法和字段值。但是在某些特定情形下,我们想在不创建对象的情况下就可以使用特定的方法或者字段值。这时我们就需要使用static对该方法或者字段进行修饰,将其设置为静态的。静态的数据和方法与类为同一存储级别。 一旦将什么东西设定为static, 那么数据或方法就不会和那个类的任何对象相互关联。
package d20180821;
/**
* static关键字
*
* @author Administrator
*
*/
public class PrimitiveTypeTest {
public static void main(String[] args) {
/* 静态数据和方法的使用*/
int a = Test.i; // 可以直接使用类名.的形式调用 (推荐方法)
Test.staticMethod();
Test test = new Test();
int b = test.i; // 也可以实例化一个对象,在由对象句柄.的形式调用
test.staticMethod();
/* 普通数据和方法的使用*/
Test test1 = new Test();
int c = test1.b; // 只能通过实例化一个对象,再由对象句柄.的形式调用
test1.normalMethod();
}
}
class Test{
static int i;
static void staticMethod() {
}
int b;
void normalMethod() {
}
}
由于类和静态成员(方法)是同时在内存中产生的,所以类里面的普通成员是不可以直接在静态方法里面被使用的。
class Test{
static int i;
int b;
void normalMethod() {
}
static void staticMethod() {
i++;
normalMethod();
b++;
}
}
在eclipse编译器中粘贴如下代码,查看效果。你会发现 normalMethod()和b++这两行根本就不能通过编译。
7. 我的第一个Java程序
这个程序可以打印出来与当前运行的系统有关的资料。并且利用了来自Java标准库的System对象的多种方法。
package d20180820;
import java.util.Date;
import java.util.Properties;
public class FirstOne {
public static void main(String[] args) {
System.out.println(new Date());
Properties p = System.getProperties();
p.list(System.out);
System.out.println("--- Memory Usage:");
Runtime rt = Runtime.getRuntime();
System.out.println("Total Memory = "
+ rt.totalMemory()
+ " Free Memory = "
+ rt.freeMemory());
}
}
8.注释和嵌入文档
在java中注释可以分成普通注释和嵌入文档注释。
普通的注释的作用只在于方便程序员记忆程序的运行过程,起到提醒作用。
// 单行注释
/*
* 多行注释
* 多行注释
* 多行注释
*
* */
还有一种注释,在使用Javadoc生成软件文档时候尤其有用。
package d20180821;
/**
* 类注释, 通常为 /** 开头,即有两个星号跟在斜线后面作为开头 ,
* 该形式的注释有两个好处:
* ① 在程序开发过程中, 鼠标悬浮在类名上的时候,可以很清楚的看到注释内容。
* ② 通过javadoc插件可以生成针对该软件的文档,而文档的内容就是这些注释和类的成员信息
*
* @author Administrator
*
*/
public class Car {
/**车名 */
public String carName;
/**车价格*/
public double price;
/**
* javadoc生成html文件的时候是可以自动识别注释里面的标签的
* 车产地
* 您甚至可以插入一个列表:
*
* - 项目一
*
- 项目二
*
- 项目三
*
* */
public String place;
/**
* 开车
*/
public void drive() {
// 单行注释
/*
* 多行注释
* 多行注释
* 多行注释
*
* */
}
}
注意javaDoc只能为public(公共)和protected(受保护)的成员处理注释文档。而“pravite”和“友好”成员的注释会被忽略。我们看不到任何的输出。
文档标记
所有的三种类型的注释文档都可包含@see 标记 , 它允许我们引用其他类里面的文档。对于这个标记,javadoc会生成相应的Html,将其直接连接到其他文档。现将在java类中常用到的标记以表格列出
标记 |
位置 |
说明 |
@author |
类声明前 |
这个类的作者 |
@see |
类声明前、方法前、类字段前 |
关联其他类,在javadoc生成文档的时候Html链接到其他文档的位置。 |
@version |
类声明前 |
版本说明 |
@param |
方法前 |
传入的自变量 |
@return |
方法前 |
方法返回值 |
@exception |
方法前 |
表示需要抛出什么异常 |
@deprecated |
方法前 |
未来改版时可能摒弃这个方法,使用该方法时会出现变异警告 |
9.编码样式
任何一种语言都有它的编写规范,Java也不例外。规范的编写可以避免很多不必要的麻烦,也方便在团队之间进行交流合作。现将java中基本的命名规范列式如下:
项目 |
规范 |
类名 |
首字母大写,驼峰命名 |
方法名 |
首字母小写,驼峰命名 |
字段 |
首字母小写,驼峰命名 |
常量 |
全大写 |
PS: 这是初稿,后期会在初稿的基础上进行优化补充。不到之处,欢迎各位朋友指正。
如果各位朋友想了解得更加详细具体,可以关注我的公众号【码小跳】在公众号输入 “一切都是对象”。获取更多有关 《Java编程思想》 第二章 “一切都是对象”的相关资料。