# 面向对象编程
# 面向对象
面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法。
GirlFriend gf = new GirlFriend();
gf.name = "Alice";
gf.send("flowers");
2
3
现实世界中,我们定义了“人”这种抽象概念,而具体的人则是“小明”、“小红”、“小军”等一个个具体的人。所以,“人”可以定义为一个类(class),而具体的人则是实例(instance):
现实世界 | 计算机模型 | Java代码 |
---|---|---|
人 | 类/class | class Pedrson{} |
小明 | 实例/ming | Person ming = new Person{} |
public class Hero {//类
String name;//姓名
float hp;//血量
float armor;//护甲
int moveSpeed;//移动四度
/*以上代码是在设计英雄这个类,类的第一个字母大写
* 他们都有一些共同的状态,如姓名,hp,护甲,移动速度等等,称为属性
* 属性名称一般小写,如果有多个单词组成,后面单词的第一个字母大写
* 属性也是变量,需要满足变量的命名规则
*/
public static void main(String[] args) {
Hero garen = new Hero();
garen.name = "盖伦";
garen.hp = 616.28f;
garen.armor = 27.536f;
garen.moveSpeed = 350;
Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 383f;
teemo.armor = 14f;
teemo.moveSpeed = 330;
}
// 创建具体的英雄
void keng() {//坑队友
System.out.println("坑队友!");
}
//无参数,无返回类型方法
float getArmor() {//获取护甲值
return armor;
}
//无参数,有float类型的返回值方法
void addSpeed(int speed) {
//在原来的基础上增加移动速度
moveSpeed = moveSpeed + speed;
}
//有参数,float类型参数方法
void legendary() {
System.out.println("超神!");
}
float getHp() {
return hp;
}
float recovery( float blood) {
return hp = hp + blood;
}
/*
* 方法是一个类的动作行为,一般是以动词开头的
* 如果有多个单词,后面的每个单词的第一个字母使用大写
*/
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 类和对象
类是对象的模板,对象是类的具体实例
# 定义类
class是一种对象模版,它定义了如何创建实例,因此,class本身就是一种数据类型. 而instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同.
在Java中,创建一个类.例如,给这个类命名为Person
,就是定义一个class
:
class Person {
public String name;
public int age;
}
2
3
4
一个class
可以包含多个字段field
,字段用来描述一个类的特征.上面的person
类,我们定义了两个字段,命名为age
.因此,通过calss
,把一组数据汇集到一个对象上,实现了数据封装.
public
是用来修饰字符的,它表示这个字段可以被外部访问.
定义一个类
主要有三个步骤:
1、定义类名,用于区分不同的类。如下代码中 public class
后面跟的就是类名。class
是声明类的关键字,类名后面跟上大括号,大括号里面就是类的一些信息。public
为权限修饰符。
public class 类名 {
//定义属性部分(成员变量)
属性1的类型 属性1;
属性2的类型 属性2;
...
//定义方法部分
方法1
方法2
...
}
2
3
4
5
6
7
8
9
10
类的第一个字母大写. 2、编写类的属性。对象有什么,需要通过属性来表示。属性的定义是写在类名后面的大括号里,在定义属性时,要明确属性的类型。在一个类当中可以写一个或多个属性。当然也可以不定义属性。
3、编写类的方法。方法也是写在大括号里面。可以定义一个方法或多个方法,当然也可以不定义方法。
public class People{
double height;
int age;
int sex;
void cry(){
System.out.println("我在哭!");
}
void laugh(){
System.out.println("我在笑!");
}
void printBaseMes(){
System.out.println("我的身高"+height+"cm");
System.out.println("我的年龄"+age+"岁");
if (this.sex == 0){
System.out.println("我是男性!");
}
else{
System.out.println("我是女性!");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 类型变量与作用域
一个类可以包含以下类型变量:
- **局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。**变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量,包括类变量与实例变量.这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问.
- 类变量:属于成员变量,也叫静态变量,类变量也声明在类中,方法体之外,但必须声明为
static
类型,存储在方法区中,只有一份.通过类名点来访问. - 实例变量:属于成员变量,没有static修饰,属于对象的,存储在堆中,有几个对象就有几份,通过引用(对象)点来访问.
在使用时注意,成员变量可以被本类的所有方法所使用,同时可以被与本类有关的其他类所使用。而局部变量只能在当前的方法中使用。
在这里我们要讲到一个关于作用域的知识了。作用域可以简单地理解为变量的生存期或者作用范围,也就是变量从定义开始到什么时候消亡。
局部变量的作用域仅限于定义它的方法内。而成员变量的作用域在整个类内部都是可见的。
同时在相同的方法中,不能有同名的局部变量;在不同的方法中,可以有同名的局部变量。
成员变量和局部变量同名时,局部变量具有更高的优先级。
--使用的时候默认采取就近原则
--若想访问同名成员变量,则this不能省略
Student zs = new Student("liyufnei",25,"hami")
class Student{
String name;//成员变量(在整个类中)
int age;
String address;
Student(String name,int age,String address){//局部变量(在当前方法中)
this.name = name;//zs.name = "liyunfei";
name = this.name;//没有意义,应该给成员变量赋值
this.age = age;//zs.age = 25;
this.address = address;//zs.address = "hami";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建实例/对象(引用)
定义了class
,只是实现了对象模板,而要根据对象模板创建出真正的对象实例,必须用new操作符.
new操作符可以创建一个实例,然后,我们定义一个引用类型的变量来指向这个实例.
定义类的时候不会为类开辟内存空间,但是一旦创建了对象,系统就会在内存中为对象开辟一块空间,用来存放对象的属性值和方法。
Person ming = new Person();
上述代码创建了一个Person
类型的实例,并通过变量ming
指向它.
如果同学们学过 C 语言,这里就和指针一样,变量 ming
保存的其实 Person 对象的引用,指向了 Person对象。
如果一个变量是基本类型,比如int hp = 50
;我们就直接管hp叫变量,=表示赋值的意思。
如果一个变量是类类型,比如 Hero h = new Hero();
我们就管h叫做引用。=不再是赋值的意思 =表示指向的意思
比如 Hero h = new Hero()
;
这句话的意思是
引用h
,指向一个Hero
对象
注意区分Person ming
是定义Person
类型的变量ming
,而new Person()
是创建Person
实例.
有了指向这个实例的变量,我们就可以通过这个变量来操作实例。访问实例变量可以用变量.字段
,例如:
ming.name = "Xiao Ming"; // 对字段name赋值
ming.age = 12; // 对字段age赋值
System.out.println(ming.name); // 访问字段name
Person hong = new Person();
hong.name = "Xiao Hong";
hong.age = 15;
2
3
4
5
6
7
上述两个变量分别指向两个不同的实例,它们在内存中的结构如下:
┌──────────────────┐
ming ──────>│Person instance │
├──────────────────┤
│name = "Xiao Ming"│
│age = 12 │
└──────────────────┘
┌──────────────────┐
hong ──────>│Person instance │
├──────────────────┤
│name = "Xiao Hong"│
│age = 15 │
└──────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
两个instance
拥有class
定义的name
和age
字段,且各自都有一份独立的数据,互不干扰。
一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。如果要定义多个public类,必须拆到多个Java源文件中。
创建对象后,我们就要使用对象了,使用对象无非就是对属性和方法进行操作和调用。语法如下
//引用对象属性
对象名.属性
//引用对象方法
对象名.方法
2
3
4
5
# 方法重载
方法的重载(overload/overloading):--------便于用户对方法的调用
发生在同一类中,方法名称相同,参数列表不同,方法体不同
编译器在编译时会根据方法的签名自动绑定调用的方法
方法重载一般用于创建一组任务相似但是参数不同的方法。
public class Test {
void f(int i) {
System.out.println("i=" + i);
}
void f(float f) {
System.out.println("f=" + f);
}
void f(String s) {
System.out.println("s=" + s);
}
void f(String s1, String s2){
System.out.println("s1+s2="+(s1+s2));
}
void f(String s, int i){
System.out.println("s="+s+",i="+i);
}
public static void main(String[] args) {
Test test = new Test();
test.f(3456);
test.f(34.56f);
test.f("abc");
test.f("abc","def");
test.f("abc",3456);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
方法重载有以下几种规则:
方法中的参数列表必须不同。比如:参数个数不同或者参数类型不同。
重载的方法中允许抛出不同的异常
可以有不同的返回值类型,但是参数列表必须不同,与返回值类型和参数名称无关.
void show(){}; void show(String name){}; //int show(){ return 1; } //编译错误,重载与返回值类型无关 //void show(String address){} //编译错误,重载与参数名称无关
1
2
3
4可以有不同的访问修饰符。
# 可变数量的参数
这时,可以采用可变数量的参数
只需要设计一个方法
public void attack(Hero ...heros)
即可代表上述所有的方法了
在方法里,使用操作数组的方式处理参数heros即可
public class ADHero extends Hero {
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}
// 可变数量的参数
public void attack(Hero... heros) {
for (int i = 0; i < heros.length; i++) {
System.out.println(name + " 攻击了 " + heros[i].name);
}
}
public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";
Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";
bh.attack(h1);
bh.attack(h1, h2);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package reflect;
import java.util.Arrays;
/**
* JDK5之后java推出了一个特性:变长参数
*/
public class ArgsDemo {
public static void main(String[] args) {
doing(1,23,"one");
doing(1,23,"one","two");
doing(1,23,"one","two","three");
doing(1,23,"one","two","three","four");
}
/**
* 变长参数只能是方法的最后一个参数,实际是一个数组类型。
*/
public static void doing(int age,long a,String... arg){
System.out.println(arg.length);
System.out.println(Arrays.toString(arg));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 方法重写
方法的重写(Override/Overriding):重新写、覆盖
发生在父子类中,方法名称相同,参数列表相同,方法体一般不同
重写方法被调用时,看对象的类型(new 的东西)----这是规定,记住就OK
class 餐馆{ void 做餐(){ 中餐 } } //1)我还是想做中餐------------不需要重写 class Aoo extends 餐馆{ } //2)我想改做西餐--------------需要重写 class Aoo extends 餐馆{ void 做餐(){ 西餐 } } //3)我想在中餐之上加西餐-------需要重写 class Aoo extends 餐馆{ void 做餐(){ super.做餐(); 西餐 } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18重写需遵循"两同两小一大"原则:-----了解即可,一般都是一模一样的
两同:
1)方法名称相同
2)参数列表相同
两小:
1)派生类方法的返回值类型小于或等于超类方法的
1.1)void时,必须相等
1.2)基本类型时,必须相等
1.3)引用类型时,小于或等于
2)派生类方法抛出的异常小于或等于超类方法的
一大:
1)派生类方法的访问权限大于或等于超类方法的
class Coo{ void show(){ double test(){return 0.0;} Student say(){return null;} Person sayHi(){return null;} } class Doo extends Coo{ //int show(){return 5;}//编译错误,void时必须相等 //int test(){return 0;}//编译错误,基本类型时必须相等 //Person say(){return null;}//编译错误,引用类型时必须小于或等于 Student sayHi(){return null;}//z } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
子类可以继承父类的方法,但如果子类对父类的方法不满意,想在里面加入适合自己的一些操作时,就需要将方法进行重写。并且子类在调用方法中,优先调用子类的方法。
比如 Animal
类中有 bark()
这个方法代表了动物叫,但是不同的动物有不同的叫法,比如狗是汪汪汪,猫是喵喵喵。
当然在方法重写时要注意,重写的方法一定要与原父类的方法语法保持一致,比如返回值类型,参数类型及个数,和方法名都必须一致。
例如:
public class Animal {
//类方法
public void bark() {
System.out.println("动物叫!");
}
}
2
3
4
5
6
7
public class Dog extends Animal {
//重写父类的bark方法
public void bark() {
System.out.println("汪!汪!汪!");
}
}
2
3
4
5
6
7
写个测试类来看看输出结果:
public class Test{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Dog d = new Dog(); // Dog 对象
Animal b = new Dog(); // Dog 对象,向上转型为Animal类型,具体会在后面的内容进行详解
a.bark();// 执行 Animal 类的方法
d.bark();//执行 Dog 类的方法
b.bark();//执行 Dog 类的方法
}
}
2
3
4
5
6
7
8
9
10
11
12
# 构造方法
构造方法:构造函数、构造器、构建器------复用给成员变量赋初值的代码
给成员变量赋初值
与类同名,没有返回值类型(连void都没有,首字母大写)
语法格式如下:
//与类同名,可以指定参数,没有返回值 public 构造方法名(){ //初始化代码 }
1
2
3
4
5下面是一个构造方法的例子:
public class People{ //无参构造方法 public People(){ } //有一个参数的构造方法 public People(int age){ } }
1
2
3
4
5
6
7
8
9
10
11又例如具体的构造方法:
public class People { //属性(成员变量)有什么 double height; //身高 int age; //年龄 int sex; //性别,0为男性,非0为女性 //构造方法,初始化了所有属性 public People(double h, int a, int s){ height = h; age = a; sex = s; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14//创建对象,调用我们自己定义的有参构造方法 People XiaoMing = new People(168, 21, 1);
1
2
3上面的例子中通过
new
关键字将类实例化成对象,而new
后面跟的就是构造方法。于是可以知道new + 构造方法
可以创建一个新的对象。在创建(new)对象时被自动调用 ,不能打点调用
若自己没有写构造方法,则编译器默认一个无参构造方法,这个构造方法什么也不会做。 若自己写了构造方法,则不再默认提供
构造方法可以重载,调用时会自动根据不同的参数选择相应的方法。
每个类都有构造方法,在创建该类的对象的时候他们将被调用.
创建一个对象的时候,至少调用一个构造方法。
通过一个类创建一个对象,这个过程叫做实例化 实例化是通过调用构造方法(又叫做构造器)实现的
比如在新建一个对象 new Object()
,括号中没有任何参数,代表调用一个无参构造方法(默认构造方法就是一个无参构造方法)。
构造方法的名称必须与类名相同,一个类可以定义多个构造方法。
public class Hero {
String name;
float hp;
float armor;
int moveSpeed;
// 方法名和类名一样(包括大小写)
// 没有返回类型
//这个无参的构造方法,如果不写,就会默认提供一个无参的构造方法
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}
public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# NUll
null
:表示空,没有指向任何对象
若引用的值为null
,则该引用不能再进行任何操作了,
若操作则发生NullPointerException
空指针异常
int a = null; //编译错误,基本类型变量与null无关
所有引用类型变量-----默认值都是null
# this
this:指代当前对象,哪个对象调用方法它指的就是哪个对象
只能用在方法中,方法中访问成员变量之前默认有个this.
主要用法:为对象传参数赋值.
this的用法:
this.成员变量名-----------访问成员变量
--------当成员变量与局部变量同名时,若想访问成员变量则this不能省略
this.方法名()-------------调用方法(一般不用) //一般省略this
this()--------------------调用构造方法(很少用) //在一个构造方法中,调用另一个构造方法
public Person(String name) { this(name, 18); // 调用另一个构造方法Person(String, int) } public Person() { this("Unnamed"); // 调用另一个构造方法Person(String) }
1
2
3
4
5
6
7
this
关键字代表当前对象。使用 this.属性
操作当前对象的属性,this.方法
调用当前对象的方法。
用 private
修饰的属性,必须定义 getter 和 setter 方法才可以访问到 (Eclipse 和 IDEA 等 IDE 都有自动生成 getter 和 setter 方法的功能)。
如下:
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
2
3
4
5
6
7
创建好了 getter 和 setter 方法后,我们发现方法中参数名和属性名一样。
当成员变量和局部变量之间发生冲突时,在属性名前面添加了 this
关键字。 此时就代表将一个参数的值赋给当前对象的属性。同理 this
关键字可以调用当前对象的方法。
# 传参
在Java中,参数传递是值的传递,按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
在Java里面只有基本类型和按照下面这种定义方式的String是按值传递,其它的都是按引用传递。就是直接使用双引号定义字符串方式:String str = “Java私塾”;
固定的参数在构造方法中写死即可,灵活的参数才需要在构造方法的参数列表中列出,传入方法体内.
方法体内通过this.参数
调用类中,方法体外的成员变量.
方法体内通过super(参数1,参数2,参数3);
调用父类的构造方法,将参数传给父类构造方法,父类再通过this.参数
调用父类中,方法体外的成员变量.固定的数据会在调用父类构造方法时直接返回.
# 包
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包的作用
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
包采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
1)同包中的类可以直接访问, 不同包中的类不能直接访问,若想访问:
1.1)先import声明类再访问类----建议
1.2)类的全称------------------太繁琐、不建议
定义包语法:
package 包名.类名
//注意:必须放在源程序的第一行,包名可用"."号隔开,类名可以用*表示,b
2
3
例如:
//在定义文件夹的时候利用"/"来区分层次
//包中用"."来分层
package com.shiyanlou.java
2
3
4
不仅是我们这样利用包名来区分类,系统也是这样做的。
系统中的包 java.(功能).(类) java.lang.(类) 包含java语言基础的类 java.util.(类) 包含java语言中各种工具类 java.io.(类) 包含输入,输出相关功能的类
如何在不同包中使用另一个包中的类?
使用 import
关键字。比如要导入包 com.shiyanlou
下 People
这个类,import com.shiyanlou.People;
。同时如果 import com.shiyanlou.*;
这是将包下的所有文件都导入进来,*
是通配符。
包的命名规范是全小写字母拼写。
若类名为唯一标识
上千个类----------------冲突几率非常大
# 访问修饰符
# 类之间的关系
以Hero
为例
自身:指的是Hero
自己;
同包子类:ADHero
这个类是Hero
的子类,并且和Hero
处于同一个包下;
不同包子类:Support
这个类是Hero
的子类,但是在另一个包下;
同包类:GiantDragon
这个类和Hero
是同一个包,但是彼此之间没有继承关系;
其他类:Item
这个类,在不同包,也没有继承关系的类
# 访问修饰符
代码中经常用到 private
和 public
修饰符,权限修饰符可以用来修饰属性和方法的访问范围。
如图所示,代表了不同的访问修饰符的访问范围,比如 private
修饰的属性或者方法,只能在当前类中访问或者使用。默认
是什么修饰符都不加,默认在当前类中和同一包下都可以访问和使用,代表package
friedly
default
.protected
修饰的属性或者方法,对同一包内的类和同包子类可见。public
修饰的属性或者方法,对所有类可见。
我们可以举一个例子,比如 money
,如果我们用 private
修饰代表着这是私有的,只能我自己可以使用。如果是 protected
代表着我可以使用,和我有关系的人,比如儿子也可以用。如果是 public
就代表了所有人都可以使用。
# 什么情况下该用什么修饰符
从作用域来看,public能够使用所有的情况。 但是大家在工作的时候,又不会真正全部都使用public,那么到底什么情况该用什么修饰符呢?
- 属性(数据/成员变量)通常使用private封装起来
- 方法(行为)一般使用public用于被调用
- 会被子类继承的方法,通常使用protected
- package(即默认)用的不多,一般新手会用package,因为还不知道有修饰符这个东西
再就是作用范围最小原则 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了
注意:
- 类的访问权限只能是public或默认的
- 类中成员的访问权限如上4种都可以
# 类属性
# 静态变量
- 由static修饰
- 属于类,存储在方法区中,只有一份
- 常常通过类名点来访问
- 何时用:所有对象所共享的数据(图片、音频、视频等)
Java 中被 static
修饰的成员称为静态成员或类成员,又叫做静态属性。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享,所有的对象,都共享一个值。静态成员可以使用类名直接访问,也可以使用对象名进行访问。
对象属性:又叫做实例属性,非静态属性.
如:
public class StaticTest{
public static String string="shiyanlou";
public static void main(String[] args){
//静态成员不需要实例化 直接就可以访问,建议使用这一种
System.out.println(StaticTest.string);//类.类属性
//如果不加static关键字 需要这样访问
StaticTest staticTest=new StaticTest();//new 对象
System.out.println(staticTest.string);//对象.类属性
//如果加上static关键字,上面的两种方法都可以使用
}
}
2
3
4
5
6
7
8
9
10
11
12
与对象属性对比:
不同对象的 对象属性 的值都可能不一样。
比如盖伦的hp
和 提莫的hp
是不一样的。
但是所有对象的类属性的值,都是一样的
# 什么时候使用对象属性,什么时候使用类属性
如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
# 类方法
# 静态方法
- 由static修饰
- 属于类,存储在方法区中,只有一份
- 常常通过类名点来访问
- 静态方法中没有隐式this传递,在静态方法中不能直接访问实例成员
- 何时用:方法的操作与对象无关
被 static
修饰的方法是静态方法,静态方法不依赖于对象,不需要将类实例化便可以调用,由于不实例化也可以调用,所以不能有 this
,也不能访问**非静态成员变量和非静态方法。**但是非静态成员变量和非静态方法可以访问静态方法。访问类方法,不需要对象的存在,直接就访问
public class StaticDemo{
int a;//实例变量(对象点来访问)
static int b;//静态变量(类名点来访问)
void show(){//有隐式this
System.out.println(a);//System.out.println(this.a);
System.out.println(b);//System.out.println(Moo.b);
}
static void test(){//没有隐式this
System.out.println(a);//编译错误
System.out.println(b);//System.out.println(Moo.b);
}
//静态方法中没有隐式this传递
//没有this就意味着没有对象
//而实例变量a必须通过对象点来访问
//所有如下代码发生编译错误
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
对象方法:又叫实例方法,非静态方法.访问一个对象方法,必须建立在有一个对象的前提的基础上.
package charactor;
public class Hero {
public String name;
protected float hp;
//实例方法,对象方法,非静态方法
//必须有对象才能够调用
public void die(){
hp = 0;
}
//类方法,静态方法
//通过类就可以直接调用
public static void battleWin(){
System.out.println("battle win");
}
public static void main(String[] args) {
Hero garen = new Hero();
garen.name = "盖伦";
//必须有一个对象才能调用,s
garen.die();
Hero teemo = new Hero();
teemo.name = "提莫";
//无需对象,直接通过类调用,静态方法
Hero.battleWin();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 调用类方法
和访问类属性一样,调用类方法也有两种方式:
对象.类方法
garen.battleWin()
类.类方法
Hero.battleWin()
,建议使用这种方式进行
# 什么时候设计对象方法,什么时候设计类方法
如果在某一个方法里,调用了对象属性,比如
public String getName(){
return name;
}
2
3
name
属性是对象属性,只有存在一个具体对象的时候,name
才有意义.如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法.
如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如
public static void printGameDuration(){
System.out.println("已经玩了10分50秒");
}
2
3
# 属性初始化
# 对象属性初始化
对象属性初始化有3种
- 声明该属性的时候初始化
- 构造方法中初始化
- 初始化块
package charactor;
public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;
{
maxHP = 200; //初始化块
}
public Hero(){
hp = 100; //构造方法中初始化
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 类属性初始化
类属性初始化有2种
声明该属性的时候初始化
静态初始化块
- 由static修饰
- 属于类,在类被加载时自动执行,一个类只被加载一次,所以静态块也只执行一次
- 何时用:初始化/加载静态资源(图片、音频、视频等)
package charactor;
public class Hero {
public String name;
protected float hp;
float maxHP;
//物品栏的容量
public static int itemCapacity=8; //声明的时候 初始化
static{
itemCapacity = 6;//静态初始化块 初始化
}
public Hero(){
}
public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 枚举类型
# 概念
问大家一个问题,程序执行时,字符串快,还是整形快? 答案很肯定当然int整形执行快。 再问下,在实际开发时,我们常用到需要定义男女,这时应该如何表达呢?
男、女
1、0
2
用哪个?男女字符串的优点是非常易读,开发者一看就明白;0、1整数的好处执行快 可毕竟是两种方式啊,能否两个优点都具备呢?其实编程世界里早就有:枚举类型就支持,java一开始不支持,不知道怎么想的,但终于在1.5时,姗姗来迟。 春秋冬夏四季如何表示呢?
# 没有枚举时的样子
package javase.base.enumc;
public class OldSeason {
//传统java实现方式,通过static可以直接访问,通过final实现不可更改
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int AUTUMN = 3;
public static final int WINTER = 4;
}
2
3
4
5
6
7
8
9
10
# Season.java
枚举后,是不特别简洁优美。用enum定义枚举类,直接定义枚举值就可以
package javase.base.enumc;
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
2
3
4
5
6
7
# TestEnum.java
package X;
import java.lang.annotation.ElementType;
public class M {
public static void main(String[] args) {
System.out.println(G.SPRING);
//如果枚举没有初始化,即省掉"=整型常数"时, 则从第一个标识符开始,
//顺次赋给标识符0, 1, 2 ...
System.out.println(G.WINTER.ordinal());
System.out.println(ElementType.TYPE);
}
}
enum G{
SPRING,//静态常量,分配了标识符从0开始
SUMMER,
WINTER
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 定义枚举的格式
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称
}
enum Season{
SPRING,SUNMMER,AUTUMN;
}
public enum ServiceResultEnum {
ERROR("error"),
SUCCESS("success"),
DATA_NOT_EXIST("未查询到记录!"),
PARAM_ERROR("参数错误!"),
SAME_CATEGORY_EXIST("已存在同级同名的分类!"),
SAME_LOGIN_NAME_EXIST("用户名已存在!"),
LOGIN_NAME_NULL("请输入登录名!"),
LOGIN_NAME_IS_NOT_PHONE("请输入正确的手机号!");
}
public enum NewBeeMallOrderStatusEnum {
DEFAULT(-9, "ERROR"),
ORDER_PRE_PAY(0, "待支付"),
ORDER_PAID(1, "已支付"),
ORDER_PACKAGED(2, "配货完成"),
ORDER_EXPRESS(3, "出库成功"),
ORDER_SUCCESS(4, "交易成功"),
ORDER_CLOSED_BY_MALLUSER(-1, "手动关闭"),
ORDER_CLOSED_BY_EXPIRED(-2, "超时关闭"),
ORDER_CLOSED_BY_JUDGE(-3, "商家关闭");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 枚举类型应用
# 定义无参枚举
enum Gender{//Gender.class
MALE,FEMALE,NONE;
}
class Product{
/**性别要求*/
private Gender gender=Gender.NONE;
public void setGender(Gender gender) {
this.gender = gender;
}
}
2
3
4
5
6
7
8
9
10
# 定义含有带参枚举
- 构建带参构建函数
private String name;
private Sex(String name){
this.name=name;
}
2
3
4
- 执行带参构造函数
MALE("男"),FEMALE("女");
- 构建get方法
public String getName() {
return name;
}
2
3
- 完整
enum Sex{
//枚举类型的对象是在类加载时创建
MALE("男"),FEMALE("女");//执行带参构造函数
private String name;
private Sex(String name){
this.name=name;
}
public String getName() {
return name;
}
}
2
3
4
5
6
7
8
9
10
11
public enum NewBeeMallOrderStatusEnum {
DEFAULT(-9, "ERROR"),
ORDER_PRE_PAY(0, "待支付"),
ORDER_PAID(1, "已支付"),
ORDER_PACKAGED(2, "配货完成"),
ORDER_EXPRESS(3, "出库成功"),
ORDER_SUCCESS(4, "交易成功"),
ORDER_CLOSED_BY_MALLUSER(-1, "手动关闭"),
ORDER_CLOSED_BY_EXPIRED(-2, "超时关闭"),
ORDER_CLOSED_BY_JUDGE(-3, "商家关闭");
private int orderStatus;
private String name;
NewBeeMallOrderStatusEnum(int orderStatus, String name) {
this.orderStatus = orderStatus;
this.name = name;
}
public static NewBeeMallOrderStatusEnum getNewBeeMallOrderStatusEnumByStatus(int orderStatus) {
for (NewBeeMallOrderStatusEnum newBeeMallOrderStatusEnum : NewBeeMallOrderStatusEnum.values()) {
if (newBeeMallOrderStatusEnum.getOrderStatus() == orderStatus) {
return newBeeMallOrderStatusEnum;
}
}
return DEFAULT;
}
public int getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(int orderStatus) {
this.orderStatus = orderStatus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public enum ServiceResultEnum {
ERROR("error"),
SUCCESS("success"),
DATA_NOT_EXIST("未查询到记录!"),
PARAM_ERROR("参数错误!"),
SAME_CATEGORY_EXIST("已存在同级同名的分类!"),
SAME_LOGIN_NAME_EXIST("用户名已存在!"),
LOGIN_NAME_NULL("请输入登录名!"),
LOGIN_NAME_IS_NOT_PHONE("请输入正确的手机号!"),
LOGIN_PASSWORD_NULL("请输入密码!"),
LOGIN_VERIFY_CODE_NULL("请输入验证码!"),
LOGIN_VERIFY_CODE_ERROR("验证码错误!"),
SAME_INDEX_CONFIG_EXIST("已存在相同的首页配置项!"),
GOODS_CATEGORY_ERROR("分类数据异常!"),
SAME_GOODS_EXIST("已存在相同的商品信息!"),
GOODS_NOT_EXIST("商品不存在!"),
GOODS_PUT_DOWN("商品已下架!"),
SHOPPING_CART_ITEM_LIMIT_NUMBER_ERROR("超出单个商品的最大购买数量!"),
SHOPPING_CART_ITEM_NUMBER_ERROR("商品数量不能小于 1 !"),
SHOPPING_CART_ITEM_TOTAL_NUMBER_ERROR("超出购物车最大容量!"),
SHOPPING_CART_ITEM_EXIST_ERROR("已存在!无需重复添加!"),
LOGIN_ERROR("登录失败!"),
NOT_LOGIN_ERROR("未登录!"),
ADMIN_NOT_LOGIN_ERROR("管理员未登录!"),
TOKEN_EXPIRE_ERROR("无效认证!请重新登录!"),
ADMIN_TOKEN_EXPIRE_ERROR("管理员登录过期!请重新登录!"),
USER_NULL_ERROR("无效用户!请重新登录!"),
LOGIN_USER_LOCKED_ERROR("用户已被禁止登录!"),
ORDER_NOT_EXIST_ERROR("订单不存在!"),
ORDER_ITEM_NOT_EXIST_ERROR("订单项不存在!"),
NULL_ADDRESS_ERROR("地址不能为空!"),
ORDER_PRICE_ERROR("订单价格异常!"),
ORDER_ITEM_NULL_ERROR("订单项异常!"),
ORDER_GENERATE_ERROR("生成订单异常!"),
SHOPPING_ITEM_ERROR("购物车数据异常!"),
SHOPPING_ITEM_COUNT_ERROR("库存不足!"),
ORDER_STATUS_ERROR("订单状态异常!"),
OPERATE_ERROR("操作失败!"),
REQUEST_FORBIDEN_ERROR("禁止该操作!"),
NO_PERMISSION_ERROR("无权限!"),
DB_ERROR("database error");
private String result;
ServiceResultEnum(String result) {
this.result = result;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 枚举的作用
为了做信息的标志和信息的分类