习惯使用word做笔记,转到博客上来的时候总会有错位,标题号错乱的情况,凑合看看文字,等我改排版
此篇是比较详细的java类讲解,易错点用红色标示了,大家注意啊
Java程序是面向对象的编程,一切程序从类开始。
类是对一系列具有相同属性的对象的抽象的描述。
类 :人类
对象 :小明,是一个具体的人类的实例(个体)
所有的东西都是对象,包括类也是对象。
-
面向对象的三大特性
- 封装
合理隐藏、合理暴露。即隐藏实现细节、暴露简单的操作。
通常封装要把细节隐藏,直接访问成员变量,就是使用细节,直接使用细节有太多的不可控情况发生。
为了隐藏,java提供了四个访问权限(有三个权限修饰符)
public : 公共的,任何地方都可以看到、访问到
protected : 受保护的,之后子类、当前包、当前类可以访问到
无 :默认的,包权限,只有当前包、当前类可以访问
private : 私有的,只有当前类可以访问
eg.1
有如下文件结构和代码
User.java
packageorg.fkjava;
publicclassUser { //包权限 intage; //私有权限 private Stringname; //受保护权限 protected Stringgender; //公开权限 publicintheight; } |
TestUser.java
packageorg;
importorg.fkjava.User;
publicclassTestUser { publicstaticvoid main(String[]args) { Useru =newUser();
u.age = -100; u.name ="张三"; u.gender ="男"; u.height = 150; } } |
运行结果:
age、name和gender三个变量都不可访问,因为TestUser.java在org包内,User.java在org.fkjava包内,两个类不在一个包内。
eg.2
将文档结构改为
TestUser.java的代码第一行改为:
packageorg.fkjava;
importorg.fkjava.User;
publicclassTestUser { publicstaticvoid main(String[]args) { Useru =newUser();
u.age = -100; u.name ="张三"; u.gender ="男"; u.height = 150; } } |
运行结果:
此时User.java和TestUser.java在同一个包内,所以age和gender可以访问,但是name受private修饰,只有当前类可以访问,而TestUser是另一个类,所以不可以访问。
- 封装类型
把基本类型,包装成对象(引用类型)
byte: Byte
short: Short
int: Integer
long: Long
char: Character
float: Float
double: Double
boolean: Boolean
封装类型的好处
1.基本类型必须有默认值,封装类型未赋值时为null。
2.封装类型具有面向对象编程的特性(封装、继承、多态)。
使用封装类型的时候,不要再使用 > < == !=之类来进行判断(之前出过bug),而使用equals等方法
- 常用方法
Integer.parseInt(Strings):把String转换为int
Byte.parseByte(Strings):把String转换为byte
...
除了Character里面没有parseChar方法以外,其他的类,都有对应想parse开头的方法。
- 基本类型转换封装类型
- 使用构造器,不推荐使用
Integer i1 = new Integer(10000);
- 使用静态方法返回一个Integer,java 5之前的推荐方法
Integer i2 = Integer.valueOf(String/int);
Integer i2 = Integer. valueOf (10000);
- java 5以后,支持自动装箱的功能,可以直接把基本类型赋予给封装类型
//装箱 =基本类型 ->封装类型
Integer i3 = 10000;
java5以后,还能支持自动拆箱
//拆箱=封装类型->基本类型,i3封装类型,i4基本类型,自动拆箱
int i4 = i3;
- 继承
子类继承父类,会得到父类的一切,但是除了构造器和私有成员外。但在调用子类构造器的时候,系统会自动调用父类的构造器,但是构造器并未被继承。
- 语法
[public] [ abstract | final ] class <类名> [ extends父类 ]
{
}
extends 是一个动词,表示扩展父类。
- 规则
- Java是单继承的,一个类只能继承一个父类。
- 所有的类,都是Object类的子类或者子孙类。
- 如果没有自己使用extends关键字进行继承,默认继承java.lang.Object类(所有的都是对象——因为所有的类,最终都是继承了Object)。
- 子类继承了父类,会得到父类的一切成员变量和方法。除private修饰外,其余都可以访问。
- 子类继承父类以后,如果父类的方法不能满足子类的需求,可以覆盖(重写、Override)父类的方法,实现扩展。
- 方法重写/覆盖(override)
两个相同:子类方法与父类方法的方法名相同;形参列表相同。
两个相同或更小:返回值类型相同或范围更小(子类);子类方法声明抛出的异常相同或更小
一个相同或更大:访问权限(权限修饰符)要相同或更大。eg.父类方法用protected,子类用public
- super关键字
引用父类的实例变量
引用父类的实例方法
调用父类的构造器
- super和this差别
super调用/引用和this调用/引用是一样的,只是this从当前类开始找,super从父类开始找。它们都表示当前实例。
this调用表示调用当前类的其他构造器,super调用则表示调用父类的构造器。
this调用和super调用不能同时出现在同一个构造器里面,因为它们都是必须在第一行!
注意:如果程序员没有在构造器的第一行写this调用或者super调用,系统会生成一个无参的super调用。
如果父类没有无参构造器,子类必须使用super调用传入合适的参数,来明确调用父类的构造器。
3.多态
同一个类型的对象,执行相同的操作,呈现不同的行为。
-
- 举例
现在有两台电脑,一台实际上是笔记本,一台是服务器,开机是它们共有的方法(操作),笔记本开机通常只需要1分钟,服务器需要半个小时(行为不同)。
publicclassTestComputer { publicstaticvoid main(String[]args) { //因为Computer包含了Notepad和Server,所以Notepad和Server在Computer的范围内(范围比较小),可以赋予给范围比较大的变量。 Computer c1 = new Notepad(); //Server是服务器,属于电脑的一个子类,Server的范围比较小。 Computer c2 = new Server();
//c1和c2都是Computer类型的变量,但是实际运行的时候,它们并不是Computer这个父类,而是分属两个子类。 c1.powerUp(); c2.powerUp(); } }
//电脑 class Computer { //有个开机的方法 publicvoid powerUp() { System.out.println("Computer里面的电脑正在开机:" + this); } } class Notepadextends Computer { //笔记本开机,要呈现一个不同行为,必须把父类的方法覆盖 publicvoid powerUp() { //调用父类的方法 super.powerUp(); //子类单独输出一下 System.out.println("Notepad里面的电脑正在开机:" + this + ",耗时1分钟"); } } class Serverextends Computer { //服务器开机,要呈现一个不同行为,必须把父类的方法覆盖 publicvoid powerUp() { //调用父类的方法 super.powerUp(); //子类单独输出一下 System.out.println("Server里面的电脑正在开机:" + this + ",耗时30分钟"); } } |
把子类对象直接赋给父类的引用时,当运行时调用该引用类型的方法时,其方法行为总是表现出子类的行为特征,而不是父类的行为特征,这就出现了同一引用调用引用变量的方法时,表现出不同的特征,这就是多态。
- 编译时类型和运行时类型
Java中的许多对象(一般都是具有父子类关系的父类对象)在运行时都会出现两种类型:编译时类型和运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。例如:Computerc1 = new Notepad()这行代码将会生成一个Computer变量,该变量的编译时类型是Computer,运行时类型是Notepad。
编译时类型用于在用javac的时候检查语法是否正确、成员(变量、方法、构造器)是否存在。
运行时,变量的具体类型可以根据等号赋予的实际对象来决定。
因此,编译Java代码时,引用变量只能调用声明该变量所用类(父类里,即Computer)里包含的方法,否则编译报错。与方法不同的是,对象的属性则不具备多态性。通过引用变量来访问其包含的实例属性时,系统总是试图访问它编译时类所定义的属性,而不是它运行时所定义的属性。
eg.将上节代码中父类Computer的powerUp方法改为powerUpNew,虽然Notepad和Server中有powerUp方法,但则报错
publicclassTestComputer { publicstaticvoid main(String[]args) { Computer c1 = new Notepad(); Computer c2 = new Server();
//编译时c1和c2都是Computer类型的变量(实际运行的时候,c1是Notepad型,c2是Server型) //所以编译时报错没有为Computer类型定义此方法 c1.powerUp(); c2.powerUp(); } }
//电脑 class Computer { publicvoidpowerUpNew() { System.out.println("Computer里面的电脑正在开机:" + this); } } class Notepadextends Computer { publicvoid powerUp() { super.powerUpNew(); System.out.println("Notepad里面的电脑正在开机:" + this + ",耗时1分钟"); } } class Serverextends Computer { publicvoid powerUp() { super.powerUpNew(); System.out.println("Server里面的电脑正在开机:" + this + ",耗时30分钟"); } } |
- 类型转换
小类可以自动转换为大类的类型(范围小的(子类)对象,可以赋予给范围大的(父类)变量)。
大类变量,要转换为小类变量,需要强制类型转换。(范围大的(父类)对象,转换为范围小的(子类)变量)。
为了避免转换时发生错误,可以用instanceof语句判断
eg.c1可以转换为Notepad类,c2不可以
Computer c1 = new Notepad(); Computer c2 = new Server();
booleanb1 =c1instanceof Notepad;//true booleanb2 =c2instanceof Notepad;//false |
但是使用instanceof时必须保证有继承关系,即instanceof后面的数据类型,应该是instanceof之前的变量的类型的子类
booleanb1 =c2instanceof String;
这样写就不行,因为c2是Computer类型,并不是String的父类。
- 类的语法
[public] [abstract | final] class <类名>
{
//类里面的组成部分
}
public : 访问修饰符,使用public修饰的类,java文件的文件名必须和类名相同。
abstract | final |这两个不能同时存在
一个java文件可以写多个的类,但是只能有一个public修饰的类,每个类都可以有main方法。
- 类里面的组成部分:
成员变量
方法
构造器
代码块
内部类、内部接口、内部枚举
- 类的执行方法
程序执行的时候,先通过java命令找到类名对应的class文件,把class文件加载到内存里面。
类被加载以后,就会到类里面查找main方法,参数必须是String[]的,找到方法后,执行方法。
1.找类
2.找类里面的main方法
3.调用main方法
publicclassTestHuman { publicstaticvoid main(String[]args) { System.out.println("TestHuman"); } }
class Human { publicstaticvoid main(String[]args) { System.out.println("Human"); } } |
在命令行里输入java TestHuman
输出
TestHuman
输入java Human
输出
Human
- 成员变量
<数据类型> <变量名> [=初始值];
成员变量通常可以认为是"人"的属性。
当变量写到方法里面的时候,变量是局部变量,仅在方法内有效。
当变量写到类里面的时候(方法外面),变量就是成员变量,在整个类里面都可以访问到。
成员变量多了一些修饰符
[public | protected | private] [static] [final] [transient] <数据类型> <变量名> [= 初始值];
权限修饰符,三选一
public | protected | private
静态范围标记
static
如果有static修饰表示属于类;没有static修饰属于实例。使用了static的变量,不管创建几个实例,所有实例中的这个变量值都相同=最后一个实例的变量值。类成员在类存在的时候,就已经存在;实例成员必须要实例存在才能存在。
eg.使用static修饰那么,两只狗的名字都输出二黄
publicclassDog { //狗的名字 static Stringname;
//狗的年龄 intage; }
class TestDog { publicstaticvoid main(String[]args) { //造第一只狗 Dog d1 = new Dog(); //第一只狗的名字 d1.name ="大黄"; d1.age = 10;
//第二只狗 Dog d2 = new Dog(); d2.name ="二黄"; d2.age = 8;
//分别输出两只狗的名字以及年龄 System.out.println(d1.name);//二黄 System.out.println(d2.name);//二黄
System.out.println(d1.age);//10 System.out.println(d2.age);//8 } } |
无static的情况,每个实例都有各自的name
有static的情况,所有实例的name值都是属于类的
final
如果有final修饰,表示变量只能赋予一次的值。
瞬间的
transient 使用此修饰符修饰的,在执行序列化的时候,不会被转换。不能和static同时使用。
- 方法
方法就是操作,在以前的面向过程的编程语言里面,通常称之为"函数",现在一些面向对象的编程语言也称之为是"函数"。
比如"门"这个类型的一个对象,可以有打开、关闭两个常见的动作,这些动作是需要被其他的(人类型的)对象使用的。
- 语法
[修饰符] <返回值类型> <方法名>([形参列表])
{
//方法体,执行语句
}
- 修饰符
public | protected | private
static
有static修饰的方法为类方法,而非实例方法,对于这个类构造的所有实例,这个方法都相同,可以通过类名来调用。没有static修饰的方法,通过对象(实例)来调用。
static修饰的方法中不能使用this引用。也不能访问非static修饰的普通成员。
final | abstract:final和static也不能同时存在
- 返回值类型
void : 特殊的返回值类型,表示不需要返回值,如果返回值类型里面不是void,那么必须包含有效的return语句。
除了void之外,任何的数据类型都可以作为返回值类型。但是只能返回一个变量。
返回什么类型的值,就该使用对应的类型的变量来接收。
如果可以强转,也可以强转后接收。
- 形参列表
方法的参数是可选的,由定义方法的时候决定。
使用方法的时候,如果方法定义了要形式参数(形参),那么必须提供实际的参数(实参)。
一个方法可以有多个参数,每个参数之间使用逗号隔开。
每个参数必须明确指定类型。
形参都不能用等号赋予值,而是在调用方法的时候,传递实参进来的。
可以将低位的变量传给高位的,eg. byte可以自动转化为int
publicclassTestMethodArguments { publicstaticvoid main(String[]args) { TestMethodArgumentst =new TestMethodArguments();
//直接给参数传递直接量 t.test1( 5 );
//给方法的参数传递变量 inti1 = 10; t.test1(i1 );
//给方法的参数传递变量,但是类型不一样 //虽然类型不同,但是byte能够自动转换为int,所以可以用 byteb1 = 15; t.test1(b1 );
//方法要的是int,但是给的是long,就不行,需要强制类型转换 longL1 = 199;
//相当于在执行test1方法之前,先执行了: //int i = (int)L1; //参数有值,然后才能执行方法 t.test1( (int)L1 ); }
//第一个测试方法,要求一个int类型的参数 void test1(inti ) { System.out.println("参数的值 : " +i ); } } |
- 参数传递机制
编程语言的参数传递方式,主要分为引用传递和值传递,而Java只能支持值传递参数。
每次把参数传递给方法、构造器的时候,始终把参数的值,复制一份给方法、构造器里面。
值传递的本质,就是把变量的值,复制一个副本,传递给方法里面。
基本类型的值,就是变量的值;
引用类型的值,是对象的内存地址。所以可以改变地址指向的内存的值
- 方法的重载(Overload)
定义:在同一个类的相同方法名,不同的形参列表,就是方法的重载。
同名方法被调用的时候,实参是什么类型的,就调用对应形参类型的方法。
规则
同一个类
相同的方法名
不同的形参列表
√形参的类型不同
√形参的类型相同,但是形参的个数不同
√形参的类型、个数都相同,但是形参的顺序不同
×形参类型、个数、顺序都相同,但是参数的名称不同
×形参类型、个数、顺序都相同,但是方法的返回值不同
×形参类型、个数、顺序都相同,但是方法的修饰符不同
- 参数可变的方法
Java 5开始提供,本质上是一个数组
- 语法
<数据类型>... <参数名>
规则
一个方法,只能有一个形参个数可变的参数
如果方法有多个参数,形参个数可变的参数,必须放到参数列表的最后
eg.
publicclassTestMultiArguments { publicstaticvoid main(String[]a) { TestMultiArgumentst =new TestMultiArguments(); //调用test方法的时候,可以直接给多个参数,也可以给一个数组
//直接给任意个数的值 //6 //[1,2,3,4,5,6] t.test(1,2,3,4,5,6);
//把数组作为参数 //5 //[5,6,7,8,9] int[]v =newint[]{5,6,7,8,9}; t.test(v); }
void test(int...a) { //a变量就是一个数组,并且数据类型是int类型的数组 System.out.println(a.length ); System.out.println( java.util.Arrays.toString(a) ); }
void test3(inta,int...j) { } } |
- 方法递归
方法执行体中,再次调用该方法本身。相当于是一种循环
规则
必须有结束点,即保证在某个时间,不要导致无限递归
必须向已知的方向递归
先调用的,后返回。
注意
每调用一个方法,就会为方法分配一个独立的栈空间(也叫方法内存、函数内存)。不断递归会造成栈内存不足,会出现"栈溢出错误(StackOverflowError)"。
- 构造器
用于产生一个对象(实例)。
Java里面不可能定义没有构造器的类,类最少会有一个构造器。如果程序员没有指定(定义)构造器,系统会自动生成一个没有参数的"默认构造器"。程序员已经指定(定义)构造器(不管是否有参数),系统都不会再生成默认的构造器。
基本上和方法差不多,只是构造器不能有返回值、不能有static修饰,不允许递归。
构造器的重载和方法的重载差不多:形参列表不同即可,构造器的名字必须和类名相同。
this调用只能放到构造器第一行代码,用于调用当前类的其他构造器。(七内详解)
this引用用于表示当前实例,调用者是哪个对象,this就表示哪个对象。(七内详解)
- 语法
[修饰符] <类名>([形参列表])
{
//不需要返回值
}
修饰符
public | protected | private
形参列表
规则和方法的形参列表的规则一样。
构造器必须通过new关键字类调用,唯一的作用就是产生实例。
每被new调用一次,构造器就执行一次,于是产生了一个实例。
- 代码块
在类里面,任何一个使用{}包起来的内容,就是代码块。
代码块的存在,是为了提高代码的重用。
- main方法执行的过程:
- 把类加载到内存里面。
- 根据名字和参数找到类里面参数为String[]类型的main方法。
-
因为main方法必须是public static修饰的,于是调用main方法等于利用类名调用方法
类名.main(args);
- 初始化代码块
在类里面、方法和构造器的外面的代码快,没有名字的{}。就是初始化代码块。
publicclassTestCodeBlock { //初始化代码块 { System.out.println("代码块1"); }
publicstaticvoid main(String[]args) { System.out.println("main"); }
//初始化代码块 { System.out.println("代码块2"); } } |
- 实例初始化代码块(非静态初始化代码块)
没有static修饰的代码块,就是属于实例的初始化代码块。
这些代码块执行的时候从上往下执行,所有的实例初始化代码块执行完成以后,才能执行构造器。每次new构造器之前都会执行一次。
publicclassTestCodeBlock {
//初始化代码块 { System.out.println("代码块1"); }
public TestCodeBlock() { System.out.println("构造器"); }
publicstaticvoid main(String[]args) { System.out.println("main");
TestCodeBlock t1 = new TestCodeBlock();
System.out.println("======================");
TestCodeBlock t2 = newTestCodeBlock(); } //初始化代码块 { System.out.println("代码块2"); } } |
运行结果:
- 类初始化代码块(静态初始化代码块)
有static修饰的,属于类的初始化代码块。
这些代码块执行的时候,依然从上往下,会在第一次主动使用类的时候,被执行一次。
eg在调用main方法的时候,其实就是第一次使用TestCodeBlock这个类,所以在此之前执行了静态代码块
publicclassTestCodeBlock {
//初始化代码块 { System.out.println("代码块1"); } static { System.out.println("静态代码块1"); } public TestCodeBlock() { System.out.println("构造器"); } publicstaticvoid main(String[]args) { System.out.println("main");
TestCodeBlock t1 = new TestCodeBlock();
System.out.println("======================");
TestCodeBlock t2 = new TestCodeBlock(); } //初始化代码块 { System.out.println("代码块2"); } static { System.out.println("静态代码块2"); } } |
总结:执行顺序:静态代码块(先父类后子类)->父类非静态代码块->父类构造器->子类非静态代码块->子类构造器
- 变量详解
5.1.局部变量
在方法里面声明的变量,就是局部变量。
局部变量包括:
方法、构造器、代码块里面声明的变量、方法和构造器的参数
规则:
局部变量必须赋予值以后才能使用,系统不会赋予默认值。
局部变量唯一可以使用的修饰符是final。加上final表示不能被再次赋值。
5.2.成员变量
没有final修饰的成员变量,系统会赋予默认值,整数为0、浮点为0.0、boolean为false、引用类型为null。
5.2.1类变量
有static修饰的成员变量,就是类变量。属于类,而不属于实例。
不推荐使用实例来访问类变量,而是直接通过类进行访问。即类名.变量(不是实例.变量)
eg.
System.out
// 公共的、静态的、最终的,数据类型类型为PrintStream的类变量,名为out
public static final PrintStream out
eg.使用方法
package shuzu;
publicclassTestClassVariable { staticinti;
publicstaticvoid main(String[]args) { //创建一个实例 TestClassVariablet2 =new TestClassVariable(); //利用实例,输出类变量的值 System.out.println(t2.i );
//把i变量的赋予为600 t2.i = 600; // t2.i在系统编译后,会自动转换为 TestClassVariable.i System.out.println(t2.i );//600
//直接通过类访问i变量 System.out.println( TestClassVariable.i );//600 } } |
5.2.2实例变量
没有static修饰的成员变量,就是实例变量。属于实例,而不属于类。
实例变量,只能通过实例来访问。每个实例,各自持有一份实例变量。
- 参数传递机制
- this关键字
永远表示当前实例
- this引用
语法:this. xxx
表示引用new的这一瞬间产生的当前实例。变量、方法的查找范围直接从当前类的实例成员里面开始找。
如果在当前类里面没有找到,会自动找父类的成员。调用者是谁,this就代表谁。
- this调用
语法:this()
用于调用当前类的其他不同参数的构造器。
一个类可以有多个不同参数的构造器,具体调用哪个构造器,通过参数列表决定。
优点:用于提高代码的重用度。软件技术的发展,就是在不断提高程序的重用度、可读性。代码的重用度越高,意味着人们写了更少的重复的代码。
this调用永远只能放在构造器的第一行代码!
eg. 使用this调用提高代码重用度
class Computer { longmemorySize; String cpuType;
Computer( longmemorySize, StringcpuType) { //变量查找的时候,使用"就近原则"查找。所以在构造器、方法里面使用变量的时候,如果参数名和成员变量名是同名的,那么会找到参数。因为参数和执行语句更靠近。 //这个等于局部变量memorySize自己赋值给自己,并没有调用到类的成员变量 memorySize =memorySize;
//===============this引用的使用方法============================================= //this永远表示当前实例 //在new的一瞬间,实例已经产生了,在构造器里面使用this.表示访问当前实例里面的成员变量。 this.memorySize =memorySize; this.cpuType =cpuType; }
//===============this调用的使用方法========================================== Computer() { //给内存大小、CPU一些默认值 //this.memorySize = 2000; //this.cpuType = "Intel i3";
//使用this调用提高代码的重用度,下面这行代码与上两行效果相同 //this(long,String)调用了构造器Computer( long memorySize, String cpuType) this(2000,"Intel i3"); //必须要使用合适的参数列表来决定调用哪个构造器(只能调用不同参数的构造器),不可以写this()!!!这样this()调用Computer(),Computer()调用this(),一直不会结束 }
}
publicclassTestComputer { publicstaticvoid main(String[]args) { } } |
- Object类
所有的类,最终都是继承了Object类,是所有类的开始。这个是Java里面唯一没有父类的类。
里面提供了所有对象都非常常用的操作(方法):
equals:比较两个对象是否相等。通过比较内存地址
源码:
publicboolean equals(Object obj) { returnthis == obj; } |
hashCode: 如果两个对象使用equals方法比较返回true,那么hashCode也应该返回相同的int值。可以认为,hashCode就是内存地址。
toString:把对象转换为String,转换为String以后才能方便输出。
getClass:返回对象所属的Class对象
重写Object的方法
上面几个方法也经常在子类中被重写(不是fianl的可以重写),比如现在要比较两个学生对象是否相等,如果认为姓名一样,学生就是同一个,这个时候需要重写equals方法(函数名和参数必须相同,即参数必须写Object obj)。
publicboolean equals(Object obj) { if(this == obj ) { //表示内存地址相等,对象一定是相等的 //这里return以后,后面还有代码是不会继续执行的 returntrue; }
//判断obj是否为Student的实例,需要强制类型转换,然后判断name是否相等 if( objinstanceof Student ) { Student s = (Student) obj; // this.name就是调用者的name // s.name就是obj是Student的时候,obj里面的name if(this.name.equals( s.name ) ) { //如果这两个name相等,表示对象相等 returntrue; } }
//其他的任何情况,都return false //obj不是Student的实例、name不相等 returnfalse; } |
此时为了保持hashcode方法返回的值也相同,重写hashcode方法,改为返回name的hashcode
//equals返回true,hashCode也应该返回相同的值 //hashCode的作用,主要就是为了识别对象的特性(属性)是否一致 publicint hashCode() { //因为比较对象是否相等的时候,就使用了String类型的name作为比较的依据 //那么在返回hashCode的时候,完全可以使用String里面的hashCode //实际上会使用Objects类里面hash方法去把所有的属性放到此方法里面去计算hash
// java 7之前没有Objects类 //没有Objects类的时候,可以选择使用 Arrays.hashCode,把要计算hash的成员全部放到里面去计算return name.hashCode();
return Objects.hash(name); } |
-
package和import
- package包,命名空间
作用:把多个同名的类,放到不同的目录。
语法
必须放到源代码的第一行,在import之前。
package <包名>;
包名是一个标识符,可以有多段组成,每段使用.隔开。
每个.相当于就是一级目录。
package org.fkjava.oa;
在这个包下面的class编译后会放到 org/fkjava/oa目录下面。
oa是项目名,src是源代码,bin是编译后的class文件
oa
src
org
fkjava
oa
workflow
User.java
hrm
User.java
TestUser.java
bin
org
fkjava
oa
workflow
User.class
hrm
User.class
TestUser.class
编译程序的命令
首先要cd到oa项目里面
-d参数表示用于指定生成的class文件放到哪个目录,默认放到当前目录。
如果有-d的参数,java命令会自动根据package生成子目录。
1.编译hrm里面的User
非Windows : javac -d bin src/org/fkjava/oa/hrm/User.java
Windows : javac -d bin src\org\fkjava\oa\hrm\User.java
2.编译workflow里面的User
非Windows : javac -d bin src/org/fkjava/oa/workflow/User.java
Windows : javac -d bin src\org\fkjava\oa\workflow\User.java
3.编译TestUser
因为使用了不是当前目录的包,并且class文件在bin目录下,所以必须使用 -cp 参数指定类的搜索目录。
cp = CLASSPATH =类路径,是java和javac命令用来搜索class文件的参数。默认是运行命令的当前目录。
如果没有指定cp参数,也可以通过,也可以通过设置CLASSPATH环境变量的方式来实现查找。
CLASSPATH的值就是类的搜索路径。多个路径使用分号隔开(非Windows使用冒号隔开)。
非Windows : javac -cp bin -d bin src/org/fkjava/oa/TestUser.java
Windows : javac -cp bin -d bin src\org\fkjava\oa\TestUser.java
4.运行TestUser
也必须通过 -cp参数指定类的搜索路径
java命令后面的类名,必须使用类的全名包名.类名格式
java -cp bin org.fkjava.oa.TestUser
import
导入另外一个包下面的类,用于简化类名。
类的全名包名.类名格式
Random类的全名是 java.util.Random,如果没有import,就必须使用全名才能使用Random。
有import的时候,就可以直接使用Random,省略掉了包名。