# 面向对象编程

# 面向对象

面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法。

GirlFriend gf = new GirlFriend();
gf.name = "Alice";
gf.send("flowers");
1
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;
	}
	/*
	 * 方法是一个类的动作行为,一般是以动词开头的
	 * 如果有多个单词,后面的每个单词的第一个字母使用大写
	 */



}
1
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;
}
1
2
3
4

一个class可以包含多个字段field,字段用来描述一个类的特征.上面的person类,我们定义了两个字段,命名为age.因此,通过calss,把一组数据汇集到一个对象上,实现了数据封装. public是用来修饰字符的,它表示这个字段可以被外部访问. 定义一个类 主要有三个步骤:

1、定义类名,用于区分不同的类。如下代码中 public class 后面跟的就是类名。class是声明类的关键字,类名后面跟上大括号,大括号里面就是类的一些信息。public 为权限修饰符。

public class 类名 {
    //定义属性部分(成员变量)
    属性1的类型 属性1;
    属性2的类型 属性2;
    ...
    //定义方法部分
    方法1
    方法2
    ...
}
1
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("我是女性!");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 类型变量与作用域

一个类可以包含以下类型变量

  • **局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。**变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量,包括类变量与实例变量.这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问.
  • 类变量:属于成员变量,也叫静态变量,类变量也声明在类中,方法体之外,但必须声明为 static 类型,存储在方法区中,只有一份.通过类名点来访问.
  • 实例变量:属于成员变量,没有static修饰,属于对象的,存储在堆中,有几个对象就有几份,通过引用(对象)点来访问.
  • image

在使用时注意,成员变量可以被本类的所有方法所使用,同时可以被与本类有关的其他类所使用。而局部变量只能在当前的方法中使用。

在这里我们要讲到一个关于作用域的知识了。作用域可以简单地理解为变量的生存期或者作用范围,也就是变量从定义开始到什么时候消亡。

  1. 局部变量的作用域仅限于定义它的方法内。而成员变量的作用域在整个类内部都是可见的。

  2. 同时在相同的方法中,不能有同名的局部变量;在不同的方法中,可以有同名的局部变量。

  3. 成员变量和局部变量同名时,局部变量具有更高的优先级。

    --使用的时候默认采取就近原则

    --若想访问同名成员变量,则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";
        
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 创建实例/对象(引用)

定义了class,只是实现了对象模板,而要根据对象模板创建出真正的对象实例,必须用new操作符. new操作符可以创建一个实例,然后,我们定义一个引用类型的变量来指向这个实例.

定义类的时候不会为类开辟内存空间,但是一旦创建了对象,系统就会在内存中为对象开辟一块空间,用来存放对象的属性值和方法。

Person ming = new Person();
1

上述代码创建了一个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;
1
2
3
4
5
6
7

上述两个变量分别指向两个不同的实例,它们在内存中的结构如下:

            ┌──────────────────┐
ming ──────>│Person instance   │
            ├──────────────────┤
            │name = "Xiao Ming"│
            │age = 12          │
            └──────────────────┘
            ┌──────────────────┐
hong ──────>│Person instance   │
            ├──────────────────┤
            │name = "Xiao Hong"│
            │age = 15          │
            └──────────────────┘

1
2
3
4
5
6
7
8
9
10
11
12
13

两个instance拥有class定义的nameage字段,且各自都有一份独立的数据,互不干扰。

一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。如果要定义多个public类,必须拆到多个Java源文件中。

创建对象后,我们就要使用对象了,使用对象无非就是对属性和方法进行操作和调用。语法如下

//引用对象属性
对象名.属性

//引用对象方法
对象名.方法
1
2
3
4
5

# 方法重载

方法的重载(overload/overloading):--------便于用户对方法的调用

  1. 发生在同一类中,方法名称相同,参数列表不同,方法体不同

  2. 编译器在编译时会根据方法的签名自动绑定调用的方法

    方法重载一般用于创建一组任务相似但是参数不同的方法。

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);
    }
}
1
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);

	}

}
1
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));
    }
}

1
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):重新写、覆盖

  1. 发生在父子类中,方法名称相同,参数列表相同,方法体一般不同

  2. 重写方法被调用时,看对象的类型(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
  3. 重写需遵循"两同两小一大"原则:-----了解即可,一般都是一模一样的

    两同:

    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("动物叫!");
    }
}

1
2
3
4
5
6
7
public class Dog extends Animal {
       //重写父类的bark方法
        public void bark() {
        System.out.println("汪!汪!汪!");
    }
}

1
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 类的方法
       }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 构造方法

构造方法:构造函数、构造器、构建器------复用给成员变量赋初值的代码

  1. 给成员变量赋初值

  2. 与类同名,没有返回值类型(连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 + 构造方法 可以创建一个新的对象。

  3. 在创建(new)对象时被自动调用 ,不能打点调用

  4. 若自己没有写构造方法,则编译器默认一个无参构造方法,这个构造方法什么也不会做。 若自己写了构造方法,则不再默认提供

  5. 构造方法可以重载,调用时会自动根据不同的参数选择相应的方法。

每个类都有构造方法,在创建该类的对象的时候他们将被调用. 创建一个对象的时候,至少调用一个构造方法。 通过一个类创建一个对象,这个过程叫做实例化 实例化是通过调用构造方法(又叫做构造器)实现的 比如在新建一个对象 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();
	}

}

1
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无关
1

所有引用类型变量-----默认值都是null

# this

this:指代当前对象,哪个对象调用方法它指的就是哪个对象

只能用在方法中,方法中访问成员变量之前默认有个this.

主要用法:为对象传参数赋值.

this的用法:

  1. this.成员变量名-----------访问成员变量

    --------当成员变量与局部变量同名时,若想访问成员变量则this不能省略

  2. this.方法名()-------------调用方法(一般不用) //一般省略this

  3. 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;
}

1
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

1
2
3

例如:

//在定义文件夹的时候利用"/"来区分层次
//包中用"."来分层
package com.shiyanlou.java

1
2
3
4

不仅是我们这样利用包名来区分类,系统也是这样做的。

系统中的包 java.(功能).(类) java.lang.(类) 包含java语言基础的类 java.util.(类) 包含java语言中各种工具类 java.io.(类) 包含输入,输出相关功能的类

如何在不同包中使用另一个包中的类?

使用 import 关键字。比如要导入包 com.shiyanlouPeople 这个类,import com.shiyanlou.People;。同时如果 import com.shiyanlou.*; 这是将包下的所有文件都导入进来,* 是通配符。

包的命名规范是全小写字母拼写

若类名为唯一标识

上千个类----------------冲突几率非常大

# 访问修饰符

# 类之间的关系

Hero为例

自身:指的是Hero自己;

同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下;

不同包子类:Support这个类是Hero的子类,但是在另一个包下;

同包类:GiantDragon这个类和Hero是同一个包,但是彼此之间没有继承关系;

其他类:Item这个类,在不同包,也没有继承关系的类

类之间的关系

# 访问修饰符

代码中经常用到 privatepublic 修饰符,权限修饰符可以用来修饰属性和方法的访问范围。

image

如图所示,代表了不同的访问修饰符的访问范围,比如 private 修饰的属性或者方法,只能在当前类中访问或者使用。默认 是什么修饰符都不加,默认在当前类中和同一包下都可以访问和使用,代表package friedly default.protected 修饰的属性或者方法,对同一包内的类和同包子类可见。public 修饰的属性或者方法,对所有类可见。

我们可以举一个例子,比如 money,如果我们用 private 修饰代表着这是私有的,只能我自己可以使用。如果是 protected 代表着我可以使用,和我有关系的人,比如儿子也可以用。如果是 public 就代表了所有人都可以使用。

# 什么情况下该用什么修饰符

从作用域来看,public能够使用所有的情况。 但是大家在工作的时候,又不会真正全部都使用public,那么到底什么情况该用什么修饰符呢?

  1. 属性(数据/成员变量)通常使用private封装起来
  2. 方法(行为)一般使用public用于被调用
  3. 会被子类继承的方法,通常使用protected
  4. package(即默认)用的不多,一般新手会用package,因为还不知道有修饰符这个东西

再就是作用范围最小原则 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来

注意

  1. 的访问权限只能是public或默认的
  2. 类中成员的访问权限如上4种都可以

# 类属性

# 静态变量

  1. 由static修饰
  2. 属于类,存储在方法区中,只有一份
  3. 常常通过类名点来访问
  4. 何时用:所有对象所共享的数据(图片、音频、视频等)

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关键字,上面的两种方法都可以使用
    }
}

1
2
3
4
5
6
7
8
9
10
11
12

与对象属性对比: 不同对象的 对象属性 的值都可能不一样。 比如盖伦的hp和 提莫的hp是不一样的。 但是所有对象的类属性的值,都是一样的

# 什么时候使用对象属性,什么时候使用类属性

如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的

如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性

# 类方法

# 静态方法

  1. 由static修饰
  2. 属于类,存储在方法区中,只有一份
  3. 常常通过类名点来访问
  4. 静态方法中没有隐式this传递,在静态方法中不能直接访问实例成员
  5. 何时用:方法的操作与对象无关

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必须通过对象点来访问
    //所有如下代码发生编译错误
    
}
1
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();
		
	}
}

1
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

# 调用类方法

和访问类属性一样,调用类方法也有两种方式:

  1. 对象.类方法

    garen.battleWin()

  2. 类.类方法

    Hero.battleWin(),建议使用这种方式进行

# 什么时候设计对象方法,什么时候设计类方法

如果在某一个方法里,调用了对象属性,比如

    public String getName(){
    	return name;
    }
1
2
3

name属性是对象属性,只有存在一个具体对象的时候,name才有意义.如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法.

如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如

 public static void printGameDuration(){
    	System.out.println("已经玩了10分50秒");
    }
1
2
3

# 属性初始化

# 对象属性初始化

对象属性初始化有3种

  1. 声明该属性的时候初始化
  2. 构造方法中初始化
  3. 初始化块
package charactor;

public class Hero {
	public String name = "some hero"; //声明该属性的时候初始化 
	protected float hp;
	float maxHP;
	
	{
		maxHP = 200; //初始化块
	}	
	
	public Hero(){
		hp = 100; //构造方法中初始化
		
	}
	
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 类属性初始化

类属性初始化有2种

  1. 声明该属性的时候初始化

  2. 静态初始化块

    1. 由static修饰
    2. 属于类,在类被加载时自动执行,一个类只被加载一次,所以静态块也只执行一次
    3. 何时用:初始化/加载静态资源(图片、音频、视频等)
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);
	}
	
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 枚举类型

# 概念

问大家一个问题,程序执行时,字符串快,还是整形快? 答案很肯定当然int整形执行快。 再问下,在实际开发时,我们常用到需要定义男女,这时应该如何表达呢?

男、女
10
1
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;
}

1
2
3
4
5
6
7
8
9
10

# Season.java

枚举后,是不特别简洁优美。用enum定义枚举类,直接定义枚举值就可以

package javase.base.enumc;

public enum Season {
	SPRING, SUMMER, AUTUMN, WINTER
}


1
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
}
1
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, "商家关闭");
}
1
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;
	}
}
1
2
3
4
5
6
7
8
9
10

# 定义含有带参枚举

  1. 构建带参构建函数
private String name;
private Sex(String name){
		this.name=name;
}
1
2
3
4
  1. 执行带参构造函数
MALE("男"),FEMALE("女");
1
  1. 构建get方法
public String getName() {
	return name;
}
1
2
3
  1. 完整
enum Sex{
	//枚举类型的对象是在类加载时创建
	MALE("男"),FEMALE("女");//执行带参构造函数
	private String name;
	private Sex(String name){
		this.name=name;
	}
	public String getName() {
		return name;
	}
}
1
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;
    }
}

1
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;
    }
}
1
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

# 枚举的作用

为了做信息的标志和信息的分类