第二章 一切都是对象
- 2.1 用引用操纵对象
- 2.2 必须有你来创建所有对象
- 2.2.1 存储到什么地方
- 2.2.2 特例,基本类型
- 2.3 永远不需要销毁对象
- 2.4 创建新的数据类型: 类
- 2.5 方法、参数和返回值
- 2.6 构建第一个Java程序
- 2.7 你的第一个Java程序
- 2.8 注释和嵌入式文档
- 2.9代码风格
- 2.11 练习
- 练习1:创建一个类,它包含一个int域和一个char域,他们都没有初始化,将他们的值打印出来,已验证Java执行了默认初始化
- 练习二:编写HelloDate.java的例子,创建一个Hello,World程序。输出这句话
- 练习三:找出含有ATypeName的代码段,将其改写成完整的程序
- 练习四:将DataOnly程序改写成一个程序
- 练习五:编写一个程序,让他含有本章定义的storage方法
- 练习七:将Incrementable的代码改写成一个程序
- 练习八:展示static域无论有多少个对象,只有一个实例
- 练习九:展示自动包装功能对所有的基本类型和包装器类型都起作用
- 练习10: 打印从命令行获得的三个参数
- 练习11:将AllTheColorsOfTheRainbow改写成一个程序
2.1 用引用操纵对象
Java一切都视为对象,使用标识符来操作对象,标识符实际是对象的一个引用。
例如:采用遥控器来控制电视机
创建一个引用,不一定需要有一个对象与之关联。
String s;
//此时拥有一个引用,但是却没有一个对象与之关联。
//但是此时向引用s返送消息,这是会返回一个错误,此时没有一个对象与之关联。
创建一个引用的使用同时进行初始化
String s = "asdf";
2.2 必须有你来创建所有对象
一旦创建了一个引用,就希望有一个对象与之关联。
❓那么如何创建一个对象呢?
通常使用new操作符。new:给我一个新的对象
String s = new String("asdf");
//不仅创建了一个新的字符串,而且进行了初始化
2.2.1 存储到什么地方
- 寄存器
- 堆栈
堆栈必须知道里面所有项的确切的生命周期,以便上下移动指针,分配或者回收空间。
这限制了程序的灵活性。
对象的引用存放在堆栈中 - 堆
堆用于存放所有的java对象,编译器不需要知道数据在堆里面具体的存活时间。 - 常量存储
- 非RAM存储
2.2.2 特例,基本类型
通过上述知道创建对象通过关键字new 来进行创建,对象存放在堆里面,引用存放在堆栈里面。
❓但是程序设计中通常使用到一系列类型,需要特殊对待,基本类型。通过new创建一个对象,特别是小的,简单的 变量,不是很有效。
解决方法 :不用newl来创建变量,而是创建一个变量,这个变量直接存储值,并且置于堆栈 中。
Java中每种基本类型所占据的空间大小
基本类型 | 大小 | 最小值 | 最大值 | 包装器类型 |
---|---|---|---|---|
boolean | – | – | – | Boolean |
char | 16 bits(2 Byte) | 0 | 2^16-1 | Charcater |
byte | 8 bits(1 Byte) | -2^7(-128) | 2^7-1(127) | Byte |
short | 16 bits(2 Byte) | -2^15 | 2^15-1 | Short |
int | 32 bits(4 Byte) | -2^31 | 2*31-1 | Integer |
long | 64bits(8 Byte) | -2^63 | 2^63-1 | Long |
float | 32bits(4 Byte) | Float | ||
double | 64bits(8 Byte) | Double |
基本类型是直接创建一个存储值的变量,所以没有对象,但是Java是一个面向对象的程序语言。所以由包装器类,使得可以在堆里面创建一个不是基本类型的对象,用来表示对象的基本类型。
char c = 'x';
//创建一个直接存储x字符的变量c,此时是直接在堆栈里
Character ch = new Character(c);
//创建一个对象,使用字符变量c来进行初始化,对象存放在堆里面。
Character ch = new Character('x');
Java的自动包装功能能够自动的将基本类型和包装器类型进行相互转换
Character ch = 'x';
//将char类型转化成Character
char c = ch;
//将Character转换成char
高精度数字
BigInteger和BigDecimal,可以算是包装器,但是没有对应的基本类型。
没有基本类型的运算符来实现。采用方法来计算采用了时间换取精度的方法
Java中的数组
数组中常出现的错误:
- 访问数组之外的数据
- 数组没有进行初始化
Java会确保数组进行初始化,并且不会超出数组的范围。
初始化:创建一个数组对象,实际上是创建一个引用数组,每个引用都会自动的被初始化一个特定值,null。
null代表引用还没有指定任何一个对象
2.3 永远不需要销毁对象
在程序中变量需要存活多长时间,销毁对象,那么是在什么时候进行的。
2.3.1 作用域(变量)
作用域决定了在其内定义变量的可见性和生命周期.
{
int x = 12;
{
int q = 96;
//q超出了这个范围之后,就不可以访问到了
}
}
在作用域里面定义的变量只可以用于作用域结束之前。
在Java中不可以进行重复定义变量
{
int x =12;
{
int x =56;
}
}
//这在c和c++里是被允许的,用较小的作用域来覆盖较大的作用域。在Java里面不被允许
2.3.2 对象的作用域
Java对象和基本类型的生命周期是不一样的。
当用new来创建一个对象的时候,可以存活于作用域之外。
{
String s = new String ("asdf");
}
引用s在作用域结束之后就访问不到了。但是s指向的对象仍然占据着空间。
超出了作用域之后在访问这个对象,因为他的唯一引用已经超出了范围。
new创建的对象,会一直保留。
Java里面有一个垃圾回收器,用来监视new创建的所有对象,并辨别不会再被引用的对象,释放内存空间。
2.4 创建新的数据类型: 类
通过引用可以指向一个对象
通过new可以创建一个对象
❓那么对象是由什么决定的呢
类决定了对象的外观和行为。
通过class关键字来创建一个类
class:我告诉你一种新类型的对象的样子
class ATypeName{
/*
class body
*/
}
现在已经创建了一种新的类型,可以通过new关键字来创建这种类型的对象了。
ATypeName a = new ATypeName();
此时还没有定义类的方法,还没有办法让这个类做更多的事情。
2.4.1字段和方法
定义一个类(编写Java程序的主要工作是定义一个类,产生类的对象,发送消息给对象),就可以让类做更多的事情。通过定义字段和方法。
字段:可以是任何类型的对象,通过引用和其通信。也可以是基本类型。
class DataOnly{
int i;
double d;
boolean b;
}
现在这个类还什么都不可做,除了存储数据,但是可以创建他的一个对象,给里面的字段进行赋值。
DataOnly data = new DataOnly();
引用对象的成员:在对象引用的后面紧接着一个句点,然后是对象内部的成员名称。
objectReference.member
data.i = 47;
data.d = 1.1;
data.b = false;
对象中也可以包含是对象类型的字段
//对象的引用myPlane
//对象里面的对象字段的引用名leftTank
//对象lefttank类型的字段是capacity
myPlane.leftTank.capacity = 100;
此时DataOnly除了保存数据之外没有别的用处,没有任何成员方法。
了解成员方法之前,先了解参数和返回值
基本成员默认值
类的某个成员是基本类型,即使没有初始化,那么java也会确保他有一个默认值
基本类型 | 默认值 |
---|---|
boolean | false |
char | |
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
float | 0.0 |
double | 0.0 |
⚠只有变量作为类的成员的时候才会有默认值,作为类的方法的变量的时候不会有默认值(不适用于局部变量)
2.5 方法、参数和返回值
Java的方法决定了一个对象能够接受什么样的消息。
方法的组成部分:名称、参数、返回值、方法体。
ReturnType methodName(Argument list){
method body
}
返回值:方法执行之后返回的值
参数列表:方法接受信息的类型和名称
**方法名和参数列表(方法签名)**能够唯一的标识一个方法
Java中的方法只能作为类的一部分进行创建。所有方法只能通过对象来调用(静态方法除外),并且这个对象能够调用这个方法。当调用不存在的方法的时候,会报错。
objectName.methodName(arg1, arg2, arg3)
可以通过一个变量来接受方法的返回值
int f(){
return 1;
}
int x = a.f();
//接受变量的类型必须和返回值的类型兼容
2.5.1 参数列表
方法的参数列表指定了要传递给方法什么样的消息,这些消息采用的是对象形式。参数列表中指定每个所传递对象的类型和名称。这里面传递的实际也是引用
int storage(String s){
return s.length()*2;
}
reutrn 关键字的用法:
1.代表方法已经完成,离开此方法。
2.方法返回一个值。
如果不想返回一个值,可以使用void
boolean flag(){return true;}
double naturalLogBase()(return 2.718)
void nothing(){return;}
void nothing2(){}
2.6 构建第一个Java程序
2.6.1 名字的可见性
❓在程序的某个模块使用了一个名字,在另一个模块里面也使用了相同的名字,那么怎么样才能区分这两个名字并且防止冲突呢?
解决方法:给一个类库生成一个不会与其他名字混淆的名字,使用反转的域名。MindView.net反转之后 net.mindview.utility.foibles.
这样就保证了每一个文件都存活于自己的名字空间内,而且都一个文件内的每一个类都有唯一的标识符
2.6.2 运用其他构件
如果想在自己的程序内使用已经定义好的类,那么编译器就必须知道如何定位他。
1.如果这个类可能就在发出调用的那个源文件中,就可以直接使用这个类,即使这个类在这个文件的后面才会被定义。
2. 如果这个类位于其他文件中,如果你想使用某个特定名字的类,但是这个名字的类不止一份。或者是你想添加一个新的类,但是和程序里已有的类相冲突。
所以你必须告诉编译器你想要的那一个类究竟是什么。通过使用import关键字可以解决这个问题。
通过import倒入一个包(类库)
通过使用编译器自带的构件,就不需要写反转的域名
import java.util.ArrayList;
//导入ArrayList这个类
import java.util.*;
//导入util这个类库里面的所有类
2.6.3 static关键字
当创建一个类时,就是在描述这个类的外观和行为。通过new关键字来创建对象,这时候就会为对象分配空间,方法才能会被调用。
两种情况:
- 只想为特定域分配单一的存储空间,不考虑创建了多少个对象,甚至不创建对象(针对字段)
- 希望某个方法不与包含它的类的任何对象关联一起。不创建对象,就可以使用方法(针对方法)
通过static关键字可以解决上述两个问题。
用static修饰的字段和方法可以代表为类数据和类方法
class StaticTest{
static int i = 47;
}
static修饰的字段,无论创建了多少个对象都只有一个存储空间,所有的对象都共享同一个字段
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
st1.i 和 st2.i都指向同一个位置
使用static修饰字段有两个方法
3. 通过创建对象
4. 直接通过使用类名直接引用
StaticTest.i++;
建议使用类名来引用static变量,不仅强调了是static共享的数据,而且提供了优化。
静态方法
class Incrementable{
static void increment(){
StaticTest.i++;
}
}
调用
Incrementalbe sf = new Incrementable();
sf.increment()
//或者
Incrementable.increment();
static修饰的main方法就是在不创建任何对象的情况下就可以调用它,这个方法是程序的入口。
2.7 你的第一个Java程序
//HelloData.java
//打印当前日期的字符串
import java.util.Date;
//这里需要打印当前日期,我们使用编译器自带的构建,同时通过import关键字告诉编译器我们要使用的那个类的位置
//这是创建一个类,限制了对象的外观和行为
//类名必须和文件名相同
public class HelloDate{
//这是一个static类型的方法,可以不通过创建对象就可以直接使用
//方法的返回值类型是void,
//方法名称是main,方法的参数列表是一个String类型的数组,实际是数组的一个引用
public static void main(String args[]){
//System大写代表这是一个类名,通过类名调用一个对象,说明这个方法是静态字段
//直接有一个out对象,然后调用println()方法
System.out.println("Hello, It's");
System.out.println(new Date());
// return;
}
}
编译和运行
编译:javac 类名.java
运行:java 类名
2.8 注释和嵌入式文档
第一种注释
/*
* This is a comment
* that continues
* across lines
*/
第二种注释
// This is a one-line comment
2.8.1 注释文档
对代码文档的维护。将代码和文档连接起来,放在同一个文件里。同时还必须使用特殊的注释语法来标记文档,同时使用javadoc来提取注释。
2.8.2 语法
2.9代码风格
为了保持开发程序的一致性,代码风格规定
- 类名的首字母大写,多个单词并在一起,并且每一个都是大写
class AllTheColorsOfTheRainbow{}
- 方法、字段以及对象的引用第一个字母都是小写
class AllTheColorOfTheRainbow{
int anIntegerRepresentingColors;
void changeTheHueOfTheColor(int newHue){
}
}
2.11 练习
练习1:创建一个类,它包含一个int域和一个char域,他们都没有初始化,将他们的值打印出来,已验证Java执行了默认初始化
如果使用静态访问非静态的话,会出现报错。因为静态会先于类加载。当静态数据将在好的时候,此时类的数据还没有加载,静态里面的非静态会没有加载。
public class Demo{
//Demo.java:7: 错误: 无法从静态上下文中引用非静态 变量 i
//System.out.println(i); ^
//Demo.java:8: 错误: 无法从静态上下文中引用非静态 变量 c
//System.out.println(c);
//这是类的变量
static int i;
static char c;
//这是类的方法
public static void main(String args[]){
System.out.println(i);
System.out.println(c);
}
}
输出结果:0
null(不显示)
练习二:编写HelloDate.java的例子,创建一个Hello,World程序。输出这句话
// HelloWorld.java
public class HelloWorld{
public static void main(String args[]){
System.out.println("Hello,World");
}
}
练习三:找出含有ATypeName的代码段,将其改写成完整的程序
//ATypeName.java
public class ATypeName{
public static void main(String args[]){
ATypeName a = new ATypeName();
}
}
练习四:将DataOnly程序改写成一个程序
//DataOnly.java
public class DataOnly{
static int i;
static char c;
static boolean f;
public static void main(String args[]){
DataOnly data = new DataOnly();
data.i = 47;
data.c = 'x';
data.f = false;
System.out.println(data.i);
System.out.println(data.c);
System.out.println(data.f);
}
}
练习五:编写一个程序,让他含有本章定义的storage方法
//Demo.java
public class Demo{
public static void main(String args[]){
int i = storage("sdf");
System.out.println(i);
}
public static int storage(String s){
return s.length() * 2;
}
}
练习七:将Incrementable的代码改写成一个程序
//Incrementable.java
public class Incrementable{
static int i = 47;
public static void add(){
Incrementable.i++;
}
public static void main(String args[]){
Incrementable.add();
Incrementable i1 = new Incrementable();
System.out.println(i1.i);
Incrementable i2 = new Incrementable();
System.out.println(i2.i);
}
}
练习八:展示static域无论有多少个对象,只有一个实例
//Demo.java
public class Demo{
public static void main(String args[]){
Demo.c = 'x';
Demo d1 = new Demo();
Demo d2 = new Demo();
System.out.println(d1.c);
System.out.println(d2.c);
}
static char c;
}
练习九:展示自动包装功能对所有的基本类型和包装器类型都起作用
//Demo.java
public class Demo{
public static void main(String args[]){
//boolean
Boolean b = false;
System.out.println(b);
boolean b1 = b;
System.out.println(b1);
//byte
Byte bb = 1;
System.out.println(bb);
byte bb1 = bb;
System.out.println(bb1);
//short
Short s = 2;
System.out.println(s);
short s1 = s;
System.out.println(s1);
//int
Integer i = 3;
System.out.println(i);
int i1 = i;
System.out.println(i1);
//long
//Demo.java:29: 错误: 不兼容的类型: int无法转换为Long
//Long l = 4;
//包装器类型不可以从小范围转换到大范围
//基本类型可以
Long l = 4L;
System.out.println(l);
long l1 = l;
System.out.println(l1);
//float
Float f = 1.0f;
System.out.println(f);
float f1 = f;
System.out.println(f1);
//double
Double d = 2.9;
System.out.println(d);
double d1 = d;
System.out.println(d1);
//char
Character c = 'c';
System.out.println(c);
char c1 = c;
System.out.println(c1);
}
}
练习10: 打印从命令行获得的三个参数
//Demo.java
public class Demo{
public static void main(String args[]){
System.out.println(args[0]);
System.out.println(args[1]);
System.out.println(args[2]);
}
}
练习11:将AllTheColorsOfTheRainbow改写成一个程序
//AllTheColorOfTheRainbow.java
public class AllTheColorOfTheRainbow{
static int anIntegerRepresentingColors;
void changeTheHueOfTheColor(int newHue){
System.out.println("Java中的命名规范");
}
public static void main(String args[]){
//这里可以访问非静态方法的原因是创建了对象
AllTheColorOfTheRainbow a = new AllTheColorOfTheRainbow();
a.anIntegerRepresentingColors = 1;
System.out.println(a.anIntegerRepresentingColors);
a.changeTheHueOfTheColor(1);
}
}