“如果我们说另一种不同的语言,那么我们就会发觉一个不同的世界。”
——Luduing Wittgerstein(1889-1951)
Java是基于C++的,但相比之下,Java是一种更“纯粹”的面向对象程序设计语言
Java语言假设我们只进行面向对象的程序设计,就必须将思想转换的面向对象的世界中来。这是入门基本功,因为在Java中(几乎)一切都是对象。
1.1用引用操纵对象
每种编程语言都有自己的操作内存中元素的方法。有时候,程序员必须要注意将要处理的数据是是什么类型。
因为一切都视为对象,所以可以采用固定的语法。但操纵的标识符实际上是对象的一个“引用”(reference)。
(举例说明:String a = “宁铂”;,
- String :最基本的引用数据类型,字符串类型 (字符串可以用带引号的文本初始化)
- a : 引用(标识符:简单理解成一个名字)
- "宁铂" :可以理解为是一个对象
- 这句话可以理解为:String类型的标识符“a”引用一个名为“宁铂”的对象
)
可以将这一情形想象成用遥控器(引用)来操做电视机(对象)。只要有遥控器,就可以保持与电视机的联系。想要调节音量,实际操作的是遥控器(引用),再由遥控器来调控电视机(对象)。如果想在屋内四处走走,同时可以操控电视机,那么只需携带遥控器(引用),而不是电视机(对象)。
没有电视机,遥控器也可以单独存在。即:创建一个引用,并不一定需要一个对象与他相关联,例如:
String s;
这里创建的只是一个引用,不是对象。此时向“s” 发送一个消息,就会报错,因为这个应用并没有与任何事物关联(即,没有电视机),因此创建时最好便进行初始化。例如:
String s = “abcd”;
1.2必须由你创建所有对象
一旦创建了一个引用,就希望它能与一个新的对象相关联,通常用“new”操作符来实现这一目的。“new”关键字的意思是“给我一个新对象”如:
String s = new String(“abcd“);
它不仅表示“给我一个新对象”,而是通过提供一个初始化字符串,给出怎样产生这个String的信息。
1.2.1 储存到什么地方
1.2.2 特例:基本类型
因为“new”将对象存储在“堆”里,故用“new”创建一个对象。但是特别是小的,简单的变量,往往不是很有效。因此可以把他们想象成“基本”类型,来特殊对待。不用“new”来创建变量,而是创建一个并非引用的“自动”变量。这个变量直接存储“值”,并置于堆栈中,因此更加高效。
![八大基本类型速查表]()
所有类型都有正负号,所以不要去寻找无符号的数值类型
boolean类型所占储存空间的大小没有明确指定,仅定义为那个取字面值“true”或“false”。
基本类型具有包装器类,可以在堆中创建一个非基本类型对象,用来表示对应的基本类型。例如:
char c = 'x'; //char 是字符类型,用单引号 ‘’包裹内容。
character ch = new character(c);
也可以这样用:
character ch = new character('x');
Java SE5 的自动包装功能可以自动转换:
character ch = ‘x’;
并可以反向转换:
char c = ch;
(包装基本类型的原因将在以后章节中说明)
(这些类被称为包装器(wrapper),或者叫对象包装器。 Java有8种基本类型,有9个包装器,分别为:Intger、Long、Short、Byte、Double、Float、Character、Boolean以及Void。 前6个类都派生于一个公共的超类Number。)
高精度数字
Java提供了两个用于高精度计算的类:BigInteger(支持任意精度的整数) 和 BigDecimal(支持任何精度的定点数) 它们大致属于包装器类的范畴,但二者没有对应的基本类型。
这两个类包含的方法,提供的操作与基本类型所能执行的操作相似。也就是说,能作用于 int 和 float的操作,也同样适用于BigInteger 和 BigDecimal。只不过必须以方法调用来取代运算符方式来实现。例如:
什么是方法调用:简单来说:对象名.方法()
什么是运算符方法调用:
运算符可分为5种类型:其中 “=” 为赋值运算符
算术运算符、赋值运算符、关系运算符、逻辑运算符、位运算符。
比如 int a=3;
BigInteger b=BigInteger.valueOf(a);
则b=3;
String s=”12345”;
BigInteger c=BigInteger.valueOf(s);
则c=12345;
由于操作复杂许多,所以运行速度会比较慢。因此这种高精度的类,是选择了精度放弃了速度
1.2.3 Java中的数组(简述)
java的数组一定会被初始化,并且不能在它的范围之外被访问,这大大提高了安全性和效率,但同时也付出了少量内存开销及运行时下标检查作为代价
当创建一个数组时,实际上是创建了一个引用数组,
每个引用数组都会自动被初始化为一个特定值。,该值拥有特定的关键字 null 。Java 一旦看见 null ,就知道这个引用还没有指向莫一个对象,在使用任意引用前,都必须指定一个对象,如果试图使用还是 null 的引用,运行时就会报错
1.3 永远不需要销毁对象
变量生命周期概念:变量能存活多长时间?如果想要销毁对象,那么什么时候进行?
1.3.1 作用域
作用域决定了在其内定义的变量名的可见性和生命周期,在Java中,作用域有花括号的位置决定。
例如:
![public class Scoping { public static void main(string[] args) { //ldfiü X int x = 12 int q 967; //ldfiü X System . out. println(x) ;]()
作用域里定义的变量只能在作用于作用域结束之前
1.3.2 对象的作用域
Java对象不具备和基本类型一样的生命周期。当用 new 创建一个Java对象时,它可以存活与作用域之外
![String s = new String( original: "a string");]()
引用 s 在作用域终点就消失了,然而, s 指向的String对象任然占据内存空间(用 new 创建的对象,只要你需要会一直保存下去)。现在这一段代码在无法作用域之外访问这个对象,但后期会看到怎样传递和复制对象的引用。
如果Java让对象继续存在,靠什么防止对象填满内存空间?
java有一个垃圾回收器,它会分辨那些不会在被引用的对象,随后释放这些对象的内存空间,以便新的对象使用
1.4 创建新的数据类型:类
如果一切都是对象,那么是什么决定了某一类对象的外观与行为呢?换句话说,是什么确定了对象的类型?
大多数程序设计语言习惯用关键字 class 来表示。class 这个关键字之后紧跟着新类型的名称。
例如:
class ATypeName { /* 类的主体(你要完成的业务) */ }
这就引入了一种新的类型,尽管类的主体,仅包含一条注释。还不能让它做太多事情,但已经可以用 new 来创建这种类型的对象。
例如:
ATypeName a = new AtypeName();
1.4.1 字段和方法
一旦定义了一个类,(在Java中你所做的全部工作就是定义类,产生那些类的对象,以及发送消息给这些对象),就可以在类中设置两种类型的元素:字段(可称作:数据成员)和方法(可称作:成员函数)。
字段可以是任何类型的对象,可以以通过其引用与其进行通信,同样可以是基本类型。如果字段是对某个对象的引用,那么必须初始化该引用,一便使其与一个实际的对象(通过 new 来实现)相关联
每个对象都有用来存储其字段的空间;普通字段不能在对象间共享。
例如:具有某些字段的类
![public class TestClass { int i; double d; boolean b;]()
尽管这个类除了可以储存数据之外什么都做不了,但是任然可以创建一个对象;
!['public class TestCIass { int i; double d; boolean b; 'class TestCIass2{ public static TestCIass void args) { data = new TestCIass()]()
可以给字段赋值,但首先必须知道如何引用一个对象的成员。
具体的实现:在对象引用的名称之后紧接着一个点,然后再接着对象内部的成员(对象引用.成员)
例如:
![TestCIass data Run: Scratches and Consoles 51 TestClass2 D: . exe 47 1.1 > TestClassjava data.d = 1 1 16 -public class TestCIass { int i; double d; boolean b; -class TestCIass2{ public static void main(string[] args) { new TestCIass() ; data. i data.b 47; = true; System . out. println(data . i) ; System . out. println(data . d) ; System . out. println(data . b) ; IDEA IDEA 20 Process finished with exit code 0]()
如果想要修改的数据位于对象所包括的对象中,在这种情况之下,只需再使用连接点即可。
例如:
myPlane.leftTank.capacity = 100;
基本成员的默认值:
若类的某个成员是基本数据类型,即使没有初始化,java也会确保它获得一个默认值
| 基本类型 |
默认值 |
| boolean |
false |
| char |
‘\u0000’ (null) |
| byte |
(byte)0 |
| short |
(short)0 |
| int |
0 |
| long |
0L |
| float |
0.0f |
| double |
0.0d |
上述方法不适用 “局部”变量(即非某个类的字段)因此,如果某个方法定义中有:int x;变量 “x”可能会得到任意值,而不是被自动初始化的零。
1.5 方法、参数和返回值
TestClass类除了保存数据外没有别的用处,因为它没有任何成员方法。如果想要了解成员方法的机制,就要先了解:
参数和返回值的概念
方法的基本组成部分包括:名称、参数、返回值和方法体。
例如:
![public class Methods { ReturnType */){]()
返回类型:在调用方法之后,从方法返回的值。
参数列表:给出了要传递给方法的信息类型和名称
方法名和参数列表(合起来被称为“方法签名”)是唯一地标识出某个方法。
java中的方法只能作为类的一部分来创建。方法只能通过对象才能被调用(除static方法,它针对类调用,并不依赖对象的存在),且这个对象必须要执行这个方法调用,如果调用的对象上不具备的方法,编译时会报错。
通过对象调用方法时,需要先列出对象名,紧接着时句号(英文),然后时方法名和参数列表
例如:
![-public class Methods { public static void main(string[] args) { new Test Type(); TestType demo = In a - demo. methodName(); System . out. println(a) ; -class TestType{ int methodName(){ int x System . out. println(" I') ; return x;]()
这种调用方法的行为通常被称为“发送信息给对象”在上述例子中消息是 methodName() ,对象是 demo。
面向对象编程通常被简单归纳为:向对象发送消息
1.5.1 参数列表
方法的参数列表内是指定要传递给方法什么的信息。这些信息采用的都是对象的形式,在参数列表中必须指定每个所传递对象的类型和名字,这里的传递的实际上也是引用。(如果参数被设定为String类型,则必须传递一个String对象。否则会报错)
例如:
![Run: e TestClass TestClass2 Scratches and Consoles Methods x D: . exe public static void main(string[] args) { TestType demo = new Test Type(); int a demo. methodName( s: "$EÉ"); System . out. println(a) ; -class Test Type{ int methodName(String System . out. println(s) ; return s. length() * 2; IDEA Process finished with exit code 0]()
通过上述案例,可以了解到 return 关键字的用法:
- 代表“已经做完,离开此方法”
- 如果此方法产生了一个值,这个值要放在 return 语句后面。作为返回的数值
你可以定义方法返回任意你养要的类型,如果不想返回任何值,可以指示此方法返回 void (空)
例如:
![boolean return true; double return 2.718; void void]()
返回值是 void ,return 关键字的作用只能用来退出方法。如果返回值不是 void 那么无论何时返回,编译器都会强制返回一个正确类型的返回值
大致可以理解为:程序似乎只是一系列带有方法的对象组合,这些方法以其他对象为参数,并发送消息给其他对象
1.6 构建一个Java 程序
1.6.1 名字可见性
名字管理对任何程序设计语言来说,都是一个重要问题。如果在程序的某个模块里使用了个名字,而其他人在这个程序的另一个模块里也使用了相同的名字,那么怎样才能区分这两个名字并防止二者互相冲突呢?
Java设计者希望程序员反过来使用自己的 Internet域名,因为这样可以保证它们肯定是独无二的。由于我的域名是 Mind view.net,所以我的各种奇奇怪怪的应用工具类库就被命名为net mindviewutility foibles。反转域名后,句点就用来代表子目录的划分。
1.6.2 运用气其他构建
如果想在自己的程序里使用预先定义好的类,那么编译器就必须知道怎么定位它们。当然
这个类可能就在发出调用的那个源文件中,在这种情况下,就可以直接使用这个类—即使这
个类在文件的后面才会被定义(Java消除了所谓的“向前引用”问题)。
如果那个类位于其他文件中,又会怎样呢?你可能会认为编译器应该有足够的智慧,能够
直接找到它的位置,但事实并非如此。想像下面的情况,如果你想使用某个特定名字的类,但
其定义却不止一份(假设这些定义各不相同)。更糟糕的是,假设你正在写一个程序,在构建过
程中,你想将某个新类添加到类库中,但却与已有的某个类名冲突。
例如:案例是在源文件中,没有是使用 import ,以现在编辑器的实例,完全可以自动导包。(没仔细看,望谅解)
![public class Car { String band; void System . out. println(" +band+ " •]()
![public class Person { String name; - new Car(); Car car - void 'HighLander" ; car.band = System . out. println(name) ; car .run(); System . out. public static void main(string[] args) { new Person() Person p = - "Tom"; p. name p. travel() ;]()
为了解决这个问题,必须消除所有可能的混淆情况。为实现这个目的,可以使用关键字
import来准确地告诉编译器你想要的类是什么 import指示编译器导入一个包,也就是一个类
库(在其他语言中,一个库不仅包含类,还可能包括方法和数据;但是Java中所有的代码都必
须写在类里)。
大多时候,我们使用与编译器附在一起的Java标准类库里的构件。有了这些构件,你就不
必写一长串的反转域名。举例来说,只须像下面这么书写就行了:
import java.util. ArrayList
这行代码告诉编译器,你想使用Java的 Arraylist类。但是,utl包含了数量众多的类,有时
你想使用其中的几个,同时又不想明确地逐一声明;那么你很容易使用通配符“*”来达到这个
目的
import java.util.*;
这种一次导入一群类的方式比一个一个地导入类的方式更常用。
1.6.3 static关键字
当创建类时,就是在描述那个类的对象的外观(字段、成员变量)与行为(方法、成员方法)。
只有用 new 来创建那个类的对象,否则,实际上并未获得任何对象。
执行 new 来创建对象时,数据储存空间才会被分配,其方法才会被外界调用 。
有两种情形上述方法是无法解决的:
- 只想为某特定域分配单一储存空间,而不去考虑究竟要创建多少对象,甚至不创建任何对象
- 希望某个方法,不包含它的类的任何对象关联在一起。也就是说,即使没有创建对象,也能够调用这方法
通过 static(静态的)关键字可以满足这两方面的需求,当声明一个事物时,就意味着这个域或者方法不会与包含它的那个类的任何对象实例关联在一起。所以未创建某个类的任何对象,也可以调用其 static 方法或访问其 static 域。(域:成员变量、字段)
只需将 static关键字放在定义之前,就可以将字段或方法设定为 static 。
例如:
![-public class TestStat1c { -class StaticTest{ static int = 47]()
即使创建两个StaticTest对象,StaticTest.i也只用一份储存空间,两个对象公用一个I
也可以通过类名直接调用static变量,而非静态成员则不行
例如:
![> Method s. java Person Scoping v TestClass.java e TestClass TestClass2 > TestStaticjava Scratches and Consoles TestStatic D: . exe 47 48 48 -public class TestStatic { public static void main(string[] args) { new SiaticTeQ(); StaticTest stl - StaticTest new 9taticTest(); st2 = System . out. printIn(StaticTest .i++) ; System . out. println(stl . i) ; System . out. printIn(st2. i) ; -class StaticTest{ static int i = 47 IDEA IC Process finished with exit code 0]()
其中,++运算符对变量进行了递加的操作,此时st1.i 和 st2.i 仍具有相同的值 48。
使用类名是引用static变量的首选方式。
类似逻辑也适用于静态方法,可以通过引用莫格静态方法或使用className.method()加以引用(类名.方法名)
定义静态方法与静态变量方法相似:
例如:
![StaticTest st = st. increment() ; new StaticTest() ; StaticTest .increment() ; -class StaticTest{ static int i = 47 static void increment(){ StaticTest . i" ;]()
尽管当 static作用于某个字段时,肯定会改变数据创建的方式(因为一个 static字段对每个类来说都只有一份存储空间,而非 static字段则是对每个对象有一个存储空间),但是如果 static作用于某个方法,差别却没有那么大。 static方法的一个重要用法就是在不创建任何对象的前提下就可以调用它。正如我们将会看到的那样,这一点对定义 mainO方法很重要,这个方法是运行一个应用时的入口点。和其他任何方法一样, static方法可以创建或使用与其类型相同的被命名对象,因此, static方法常常拿来做“牧羊人”的角色,负责看护与其隶属同一类型的实例群
1.7 你的第一个Java程序
此程序开始打印一个字符串,然后打印当前日期(这里用到了Java标注库里的Data类)
![v Thinking in Java 4th v Everything_ls_an_object s rc e Car HelloData > Method s java Person Scoping v TestClass.java e TestClass TestClass2 > TestStaticjava Scratches and Consoles import java.util.*; * @AUth06 * @Date 2021/7/6 1:15 * @Version 1.0 public class He1100ata { public static void main(string[] args) { System. out. System. out. println(new Date()); Run: HelloData x D: . exe IDEA Hello, it's: Tue JUI 06 CST 2021 Process finished with exit code 0]()
类名必须和文件名相同,如果你创建一个独立运行的程序,那么文件中必须在某个类与该文件同名。否则会报错,且,必须包含 main()方法。
publicstaticvoidmain(String[]args){}
其中, public关键字意指这是一个可由外部调用的方法,maino方法的参数是一个 String对象的数组。在这个程序中并未用到args,但是Java编译器要求必须这样做,因为args要用来存储命令行参数。