JAVA学习记录

JAVA学习笔记

总结自how2j.cn

JAVA基础

HelloWorld

下载JDK→配置环境变量→检查配置是否成功java -version

创建源文件→编写基础输出命令System.out.println("hello world");→编译javac

面向对象

类:

java所有的代码都是运行在类里面的

定义:类相当于参数和方法的集合

public class HelloWorld{}
  • public 表示这是一个可以公开访问的类
  • class 表示这是一个类
  • HelloWorld 表示类的名字,每个单词的首字母大写

方法

主方法:

你会写很多代码,总有第一行执行的代码,这就是主方法,每个类都可以有主方法
args 表示运行参数,在本例中没有使用到。

public static void main(String[] args){
  System.out.println("hello world");
}

变量

基本变量类型:

整型:
类型 长度
byte 8位
short 16位
int 32位
long 64位
字符型:
类型 长度
char 16位
浮点型:
类型 长度
float 32位
double 64位
布尔型:
类型 长度
boolean 1位
String类型:

String类型其实并不是基本类型,但是它因广泛的被使用,常常被误以为是一种基本类型。

类型转换:

转换规则:

精度高的数据类型就像容量大的杯子,可以放更大的数据
精度低的数据类型就像容量小的杯子,只能放更小的数据
小杯子往大杯子里倒东西,大杯子怎么都放得下
大杯子往小杯子里倒东西,有的时候放的下,有的时候就会有溢出
需要注意的一点是
虽然short和char都是16位的,长度是一样的
但是彼此之间,依然需要进行强制转换

变量命名规则:

注:在命名的时候,尽量使用完整的单词进行命名,比如name,moveSpeed,而不是使用缩写 n,m。

1. 变量命名只能使用字母 数字 $ _
2. 变量第一个字符 只能使用 字母 $ _
3. 变量第一个字符 不能使用数字
4. 不能只使用关键字,但是可以包含关键字 

作用域:

字段,属性,Field:

当一个变量被声明在类下面
变量就叫做字段 或者属性成员变量Field
比如变量i,就是一个属性。
那么从第2行这个变量声明的位置开始,整个类都可以访问得到
所以其作用域就是从其声明的位置开始的整个类

参数:

如果一个变量,是声明在一个方法上的,就叫做参数
参数的作用域即为该方法内的所有代码
其他方法不能访问该参数
类里面也不能访问该参数

局部变量:

声明在方法内的变量,叫做局部变量
其作用域在声明开始的位置,到其所处于的块结束位置

final:

当一个变量被final修饰的时候,该变量只有一次赋值的机会

数组

定义:数组是一个固定长度的,包含了相同类型数据的容器

声明数组:

public class HelloWorld {
	public static void main(String[] args) {
		// 声明一个数组
		int[] a;
	}
}
  • int[] a; 声明了一个数组变量。
  • []表示该变量是一个数组
  • int 表示数组里的每一个元素都是一个整数
  • a 是变量名
  • 但是,仅仅是这一句声明,不会创建数组
  • 有时候也会写成int a[]; 没有任何区别,就是你看哪种顺眼的问题

创建数组:

public class HelloWorld {
    public static void main(String[] args) {
        int[] a;//声明一个引用
        a = new int[5]; //创建一个长度是5的数组,并且使用引用a指向该数组
        int[] b = new int[5]; //声明的同时,指向一个数组
    }
}

创建数组的时候,要指明数组的长度。
new int[5]
引用概念:

  • 如果变量代表一个数组,比如a,我们把a叫做引用
  • 与基本类型不同 ,int c = 5; 这叫给c赋值为5
  • 声明一个引用int[] a; a = new int[5];让a这个引用,指向数组

数组空间分配与赋值:

分配空间与赋值分步进行:
public class HelloWorld {
    public static void main(String[] args) {
        int[] a = new int[5]; //分配了长度是5的数组,但是没有赋值
         
        //没有赋值,那么就会使用默认值
        //作为int类型的数组,默认值是0
        System.out.println(a[0]);
         
        //进行赋值
        a[0] = 100;
        a[1] = 101;
        a[2] = 103;
        a[3] = 120;
        a[4] = 140;
    }
}
分配空间,同时赋值 :
public class HelloWorld {
    public static void main(String[] args) {
        //写法一: 分配空间同时赋值
        int[] a = new int[]{100,102,444,836,3236};
 
        //写法二: 省略了new int[],效果一样
        int[] b = {100,102,444,836,3236};
         
        //写法三:同时分配空间,和指定内容
        //在这个例子里,长度是3,内容是5个,产生矛盾了
        //所以如果指定了数组的内容,就不能同时设置数组的长度
        int[] c = new int[3]{100,102,444,836,3236};
         
    }
}

增强型for循环:

注:增强型for循环只能用来取值,却不能用来修改数组里的值

public class HelloWorld {
    public static void main(String[] args) {
        int values [] = new int[]{18,62,68,82,65,9};
        //常规遍历
        for (int i = 0; i < values.length; i++) {
            int each = values[i];
            System.out.println(each);
        }
         
        //增强型for循环遍历
        for (int each : values) {
            System.out.println(each);
        }
         
    }
}

复制数组:

把一个数组的值,复制到另一个数组中

System.arraycopy(src, srcPos, dest, destPos, length)

  • src: 源数组
  • srcPos: 从源数组复制数据的起始位置
  • dest: 目标数组
  • destPos: 复制到目标数组的起始位置
  • length: 复制的长度
public class HelloWorld {
    public static void main(String[] args) {
        int a [] = new int[]{18,62,68,82,65,9};
         
        int b[] = new int[3];//分配了长度是3的空间,但是没有赋值
         
        //通过数组赋值把,a数组的前3位赋值到b数组
         
        //方法一: for循环
         
        for (int i = 0; i < b.length; i++) {
            b[i] = a[i];
        }
        
        //方法二: System.arraycopy(src, srcPos, dest, destPos, length)
        //src: 源数组
        //srcPos: 从源数组复制数据的起始位置
        //dest: 目标数组
        //destPos: 复制到目标数组的启始位置
        //length: 复制的长度       
        System.arraycopy(a, 0, b, 0, 3);
         
        //把内容打印出来
        for (int i = 0; i < b.length; i++) {
            System.out.print(b[i] + " ");
        }
 
    }
}

Arrays:

Arrays是针对数组的工具类,可以进行 排序,查找,复制填充等功能。

关键字 简介
copyOfRange 数组复制
toString() 转换为字符串
sort 排序
binarySearch 搜索
equals 判断是否相同
fill 填充
数组复制:

copyOfRange(int[] original, int from, int to);

  • 第一个参数表示源数组
  • 第二个参数表示开始位置(取得到)
  • 第三个参数表示结束位置(取不到)

与使用System.arraycopy进行数组复制类似的, Arrays提供了一个copyOfRange方法进行数组复制。不同的是System.arraycopy,需要事先准备好目标数组,并分配长度。 copyOfRange 只需要源数组就就可以了,通过返回值,就能够得到目标数组了。

转换为字符串:

String content = Arrays.toString(int[] original);

  • 参数表示源数组

如果要打印一个数组的内容,就需要通过for循环来挨个遍历,逐一打印

但是Arrays提供了一个toString()方法,直接把一个数组,转换为字符串,这样方便观察数组的内容

排序

Arrays.sort(int[] original);

  • 参数表示源数组
搜索:

Arrays.binarySearch(int[] original,int targ));

  • 第一个参数表示源数组
  • 第二个参数表示搜索目标

查询元素出现的位置
需要注意的是,使用binarySearch进行查找之前,必须使用sort进行排序
如果数组中有多个相同的元素,查找结果是不确定的

判断是否相同:

Arrays.equals(int[] original1, int[] original2)

  • 两个参数表示需要判断的数组

比较两个数组的内容是否一样
第二个数组的最后一个元素是8,和第一个数组不一样,所以比较结果是false

填充:

Arrays.fill(int[] original, int targ);

  • 第一个参数表示源数组
  • 第二个参数表示填充内容

使用同一个值,填充整个数组,会覆盖原有的所有数据。

类和对象

引用:

概念:如果一个变量的类型是 类类型,而非基本类型,那么该变量又叫做引用

引用和指向:

new Hero();
代表创建了一个Hero对象
但是也仅仅是创建了一个对象,没有办法访问它
为了访问这个对象,会使用引用代表这个对象

Hero h = new Hero();

h这个变量是Hero类型,又叫做引用
=的意思指的h这个引用代表右侧创建的对象
代表” 在面向对象里,又叫做“指向

引用和对象:

多个引用,一个对象

  • 引用有多个,但是对象只有一个。
  • 对象就像 “房产”, 引用就像"房产证"。房产证的复印件可以有多张,但是真正的"房产" 只有这么一处

注:如果一个对象没有任何引用指向了换句话说,就没有任何手段控制和访问该对象,那么该对象就变得没有意义。

继承

extends

虽然Weapon自己没有设计name和price,但是通过继承Item类,也具备了name和price属性

public class Weapon extends Item{
    int damage; //攻击力
     
    public static void main(String[] args) {
        Weapon infinityEdge = new Weapon();
        infinityEdge.damage = 65; //damage属性在类Weapon中新设计的
         
        infinityEdge.name = "无尽之刃";//name属性,是从Item中继承来的,就不需要重复设计了
        infinityEdge.price = 3600;
         
    }
     
}

注:一个子类只能继承一个父类,但一个父类可以有多个子类

方法重载

方法名一样的,但参数类型不一样
在调用方法attack的时候,会根据传递的参数类型以及数量,自动调用对应的方法

可变数量的参数:

如果要攻击更多的英雄,就需要设计更多的方法,这样类会显得很累赘,像这样:

public void attack(Hero h1)

public void attack(Hero h1,Hero h2)

public void attack(Hero h1,Hero h2,Hero h3)

这时,可以采用可变数量的参数
只需要设计一个方法
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);
 
    }
 
}

构造方法

什么是构造方法 :

方法名和类名一样(包括大小写)
没有返回类型
实例化一个对象的时候,必然调用构造方法

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();
    }
 
}
隐式的构造方法:

Hero类的构造方法是

public Hero(){ }

这个无参的构造方法,如果不写,就会默认提供一个

如果提供了一个有参的构造方法:

一旦提供了一个有参的构造方法
同时又没有显式的提供一个无参的构造方法
那么默认的无参的构造方法,就没了

构造方法的重载:

和普通方法一样,构造方法也可以重载

public class Hero {
    String name; //姓名
    float hp; //血量
    float armor; //护甲
    int moveSpeed; //移动速度
    
    //带一个参数的构造方法
    public Hero(String heroname){ 
        name = heroname;
    }
     
    //带两个参数的构造方法
    public Hero(String heroname,float herohp){ 
        name = heroname;
        hp = herohp;
    }
       
    public static void main(String[] args) {
        Hero garen =  new Hero("盖伦"); 
        Hero teemo =  new Hero("提莫",383);
    }
}

this

  • this代表当前对象

  • 通过this访问属性

    • 通过this关键字访问对象的属性
  • 通过this调用其他的构造方法

    • 如果要在一个构造方法中,调用另一个构造方法,可以使用this()

包:package

  • 把比较接近的类,规划在同一个包下
  • 使用其他包下的类,必须import
    • 使用同一个包下的其他类,直接使用即可
    • 但是要使用其他包下的类,必须import

修饰符

修饰符 自身 同子包类 不同子包类 同包类 其他类
private私有的 访问 继承 继承 访问 访问
package不写 访问 继承 继承 访问 访问
protected受保护的 访问 继承 继承 访问 访问
public公共的 访问 继承 继承 访问 访问
那么什么情况该用什么修饰符呢?

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

  1. 属性通常使用private封装起来
  2. 方法一般使用public用于被调用
  3. 会被子类继承的方法,通常使用protected
  4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

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

类属性

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象都共享一个值

访问类属性:

访问类属性有两种方式

  1. 对象.类属性

teemo.copyright

  1. 类.类属性

Hero.copyright

这两种方式都可以访问类属性,访问即修改和获取,但是建议使用第二种 类.类属性 的方式进行,这样更符合语义上的理解

类方法

类方法: 又叫做静态方法

对象方法: 又叫实例方法,非静态方法

访问一个对象方法,必须建立在有一个对象的前提的基础上
访问类方法,不需要对象的存在,直接就访问

访问类方法:

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

  1. 对象.类方法

garen.battleWin();

  1. 类.类方法

Hero.battleWin();

这两种方式都可以调用类方法,但是建议使用第二种 类.类方法 的方式进行,这样更符合语义上的理解。

  • 如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法

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

  • 这样的方法,更带有功能性色彩

单例模式

定义:单例模式又叫做 Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在

饿汉式:

GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new 得到新的实例。
GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取8行定义的对象,而且每一次都是获取同一个对象。 从而达到单例的目的。
这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例

package charactor;
public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){
    }
 
    //准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
    private static GiantDragon instance = new GiantDragon();
     
    //public static 方法,提供给调用者获取8行定义的对象
    public static GiantDragon getInstance(){
        return instance;
    }
     
}
懒汉式:

懒汉式单例模式与饿汉式单例模式不同,只有在调用getInstance的时候,才会创建实例

package charactor;
public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){       
    }

    //准备一个类属性,用于指向一个实例化对象,但是暂时指向null
    private static GiantDragon instance;
      
    //public static 方法,返回实例对象
    public static GiantDragon getInstance(){
        //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
        if(null==instance){
            instance = new GiantDragon();
        }
        //返回 instance指向的对象
        return instance;
    }
      
}
什么时候使用饿汉式,什么时候使用懒汉式?

饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。
如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。

懒汉式,是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量。
使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。

看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式

单例模式三元素 :

什么是单例模式?

回答的时候,要答到三元素

  1. 构造方法私有化
  2. 静态属性指向实例
  3. public static的 getInstance方法,返回第二步的静态属性

枚举类型

预先定义的常量:

枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量

比如设计一个枚举类型 季节,里面有4种常量

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

注:因为是常量,所以一般都是全大写

使用枚举的好处:

假设在使用switch的时候,不是使用枚举,而是使用int,而int的取值范围就不只是1-4,有可能取一个超出1-4之间的值,这样判断结果就似是而非了。(因为只有4个季节)

但是使用枚举,就能把范围死死的限定在这四个当中而不会出现奇怪的 第5季

遍历枚举 :

借助增强型for循环,可以很方便的遍历一个枚举都有哪些常量

public class HelloWorld {
    public static void main(String[] args) {
        for (Season s : Season.values()) {
            System.out.println(s);
        }
    }
}

接口与继承

接口:

AD接口 ,声明一个方法 physicAttack 物理攻击,但是没有方法体,是一个“”方法

package charactor;
public interface AD {
        //物理伤害
    public void physicAttack();
}

实现某个接口,就相当于承诺了某种约定

所以,实现了AD这个接口,就必须提供AD接口中声明的方法physicAttack()
实现 在语法上使用关键字 implements

package charactor;
public class ADHero extends Hero implements AD{
    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
}

对象转型:

所谓的转型,是指当引用类型和对象类型不一致的时候,才需要进行类型转换
类型转换有时候会成功,有时候会失败

子类转父类(向上转型) :

所有的子类转换为父类,都是说得通的

Hero h = new Hero();
ADHero ad = new ADHero();
h = ad;
父类转子类(向下转型) :

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是 转换有风险,风险自担。

能够转换的案列:

1.        Hero h =new Hero();
2.        ADHero ad = new ADHero();
3.        h = ad;
4.        ad = (ADHero) h;
  • 第3行,是子类转父类,一定可以的
  • 第4行,就是父类转子类,所以要进行强转。
  • h这个引用,所指向的对象是ADHero, 所以第4行,就会把ADHero转换为ADHero,就能转换成功。

不能转换的案例:

1.        Hero h =new Hero();
2.        ADHero ad = new ADHero();
3.        Support s =new Support();
4.        h = s;
5.        ad = (ADHero)h;
  • 第4行,是子类转父类,是可以转换成功的
  • 第5行,是把h引用所指向的对象 Support,转换为ad引用的类型ADHero。 从语义上讲,把物理攻击英雄,当成辅助英雄来用,说不通,所以会强制转换失败,并且抛出异常

注:没有继承关系的两个类,不能互相转换

实现类转换成接口(向上转型) :

引用ad指向的对象是ADHero类型,这个类型实现了AD接口
从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。

package charactor;
public class Hero {
    public String name;
    protected float hp;
    public static void main(String[] args) {
        ADHero ad = new ADHero();
        AD adi = ad;
    }
}
  • 7行: 把一个ADHero类型转换为AD接口
接口转换成实现类(向下转型) :
package charactor;
     
public class Hero {
    public String name;
    protected float hp;
         
    public static void main(String[] args) {
        ADHero ad = new ADHero();
            
        AD adi = ad;
   
        ADHero adHero = (ADHero) adi;
            
        ADAPHero adapHero = (ADAPHero) adi;
        adapHero.magicAttack();
    }
         
}
  • 10行: ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功
  • 12行: adi实际上是指向一个ADHero的,所以能够转换成功
  • 14行: adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。

注:假设能够转换成功,那么就可以使用magicAttack方法,而adi引用所指向的对象ADHero是没有magicAttack方法的

instanceof :

instanceof Hero 判断一个引用所指向的对象,是否是Hero类型,或者Hero的子类

package charactor;
  
public class Hero {
    public String name;
    protected float hp;
      
    public static void main(String[] args) {
        ADHero ad = new ADHero();
        APHero ap = new APHero();
         
        Hero h1= ad;
        Hero h2= ap;
         
        //判断引用h1指向的对象,是否是ADHero类型
        System.out.println(h1 instanceof ADHero);
         
        //判断引用h2指向的对象,是否是APHero类型
        System.out.println(h2 instanceof APHero);
         
        //判断引用h1指向的对象,是否是Hero的子类型
        System.out.println(h1 instanceof Hero);
    }
}

重写:

  • 子类可以继承父类的对象方法
  • 在继承后,重复提供该方法,就叫做方法的重写 又叫覆盖 override

如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。

但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.

这样就增加了开发时间和维护成本

多态:

  • 操作符的多态
    “+” 可以作为算数运算,也可以作为字符串连接

  • 类的多态
    父类引用指向子类对象

类的多态条件 :
  1. 父类(接口)引用指向子类对象
  2. 调用的方法有重写
使用多态 :

如果物品的种类特别多,那么就需要设计很多的方法比如useArmor,useWeapon等等

这个时候采用多态来解决这个问题
设计一个方法叫做useItem,其参数类型是Item

  • 如果是使用血瓶,调用该方法
  • 如果是使用魔瓶,还是调用该方法

无论英雄要使用什么样的物品,只需要一个方法即可

package charactor;
 
import property.Item;
import property.LifePotion;
import property.MagicPotion;
   
public class Hero {
    public String name;
    protected float hp;
 
    public void useItem(Item i){
        i.effect();
    }
 
    public static void main(String[] args) {
         
        Hero garen =  new Hero();
        garen.name = "盖伦";
     
        LifePotion lp =new LifePotion();
        MagicPotion mp =new MagicPotion();
         
        garen.useItem(lp);
        garen.useItem(mp);     
         
    }
       
}

隐藏:

与重写类似,方法的重写是子类覆盖父类的对象方法

隐藏,就是子类覆盖父类的类方法

super:

super能够调用父类方法与属性

public ADHero(String name){
        super(name);
        System.out.println("AD Hero的构造方法");
}
public int getMoveSpeed(){
   return this.moveSpeed;
}
public int getMoveSpeed2(){
   return super.moveSpeed;
}

Object:

Object类是所有类的父类

Object提供的方法:
  • toString()

    • Object类提供一个toString方法,所以所有的类都有toString方法
      toString()的意思是返回当前对象的字符串表达
      通过 System.out.println 打印对象就是打印该对象的toString()返回值
  • finalize()

    • 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

      当它被垃圾回收的时候,它的finalize() 方法就会被调用。

      finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。

  • equals()

  • equals() 用于判断两个对象的内容(值)是否相同

  • 这不是Object的方法,但是用于判断两个对象是否相同
    更准确的讲,用于判断两个引用,是否指向了同一个对象

  • hashCode()

  • hashCode方法返回一个对象的哈希值

  • 线程同步相关方法

    • wait()
    • notify()
    • notifyAll()
  • getClass()

  • getClass()会返回一个对象的类对象

final:

final修饰类 :

当Hero被修饰成final的时候,表示Hero不能够被继承
其子类会出现编译错误

final修饰方法 :

Hero的useItem方法被修饰成final,那么该方法在ADHero中,不能够被重写

final修饰基本类型变量 :

final修饰基本类型变量,表示该变量只有一次赋值机会
16行进行了赋值,17行就不可以再进行赋值了

final修饰引用 :

final修饰引用
h引用被修饰成final,表示该引用只有1次指向对象的机会
所以17行会出现编译错误
但是,依然通过h引用修改对象的属性值hp,因为hp并没有final修饰

package charactor;
 
public class Hero extends Object {
        
    String name; //姓名
        
    float hp; //血量
        
    float armor; //护甲
        
    int moveSpeed; //移动速度
     
    public static void main(String[] args) {
 
        final Hero h;
        h  =new Hero();
        h  =new Hero();
         
        h.hp = 5;
         
    }
      
}
常量:

常量指的是可以公开,直接访问,不会变化的值
比如 itemTotalNumber 物品栏的数量是6个

抽象类:

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

这样的方法就叫抽象方法,使用修饰符“abstract"

当一个类有抽象方法的时候,该类必须被声明为抽象类

为Hero增加一个抽象方法 attack,并且把Hero声明为abstract的。
APHero,ADHero,ADAPHero是Hero的子类,继承了Hero的属性和方法。
但是各自的攻击手段是不一样的,所以继承Hero类后,这些子类就必须提供不一样的attack方法实现。

//Hero.java
package charactor;
 
public abstract class Hero {
    String name;
 
    float hp;
 
    float armor;
 
    int moveSpeed;
 
    public static void main(String[] args) {
 
    }
 
    // 抽象方法attack
    // Hero的子类会被要求实现attack方法
    public abstract void attack();
 
}				
//ADAPHero.java
package charactor;
 
public class ADAPHero extends Hero implements AD, AP {
 
    @Override
    public void attack() {
 
        System.out.println("既可以进行物理攻击,也可以进行魔法攻击");
    }
 
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    }
 
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
 
}
抽象类可以没有抽象方法 :

Hero类可以在不提供抽象方法的前提下,声明为抽象类
一旦一个类被声明为抽象类,就不能够被直接实例化

package charactor;
   
public abstract class Hero {
    String name;
          
    float hp;
          
    float armor;
          
    int moveSpeed;
       
    public static void main(String[] args) {
        //虽然没有抽象方法,但是一旦被声明为了抽象类,就不能够直接被实例化
        Hero h= new Hero();
    }
          
}
抽象类和接口的区别 :

区别1:

  • 子类只能继承一个抽象类,不能继承多个
  • 子类可以实现多个接口

区别2:

  • 抽象类可以定义
    • public,protected,package,private
      静态和非静态属性
      final和非final属性
  • 但是接口中声明的属性,只能是
    • public
      静态
      final的
      即便没有显式的声明

注: 抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

内部类:

内部类分为四种:

  • 非静态内部类
  • 静态内部类
  • 匿名类
  • 本地类
非静态内部类:

非静态内部类 BattleScore “战斗成绩”
非静态内部类可以直接在一个类里面定义

比如:

  • 战斗成绩只有在一个英雄对象存在的时候才有意义
  • 所以实例化BattleScore 的时候,必须建立在一个存在的英雄的基础上
  • 语法: new 外部类().new 内部类()
  • 作为Hero的非静态内部类,是可以直接访问外部类的private实例属性name的
package charactor;
 
public class Hero {
    private String name; // 姓名
 
    float hp; // 血量
 
    float armor; // 护甲
 
    int moveSpeed; // 移动速度
 
    // 非静态内部类,只有一个外部类对象存在的时候,才有意义
    // 战斗成绩只有在一个英雄对象存在的时候才有意义
    class BattleScore {
        int kill;
        int die;
        int assit;
 
        public void legendary() {
            if (kill >= 8)
                System.out.println(name + "超神!");
            else
                System.out.println(name + "尚未超神!");
        }
    }
 
    public static void main(String[] args) {
        Hero garen = new Hero();
        garen.name = "盖伦";
        // 实例化内部类
        // BattleScore对象只有在一个英雄对象存在的时候才有意义
        // 所以其实例化必须建立在一个外部类对象的基础之上
        BattleScore score = garen.new BattleScore();
        score.kill = 9;
        score.legendary();
    }
 
}
静态内部类 :

在一个类里面声明一个静态内部类
比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。
与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化
语法:new 外部类.静态内部类();
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别

匿名类 :

匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类

有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
这样的类,叫做匿名类

package charactor;
   
public abstract class Hero {
    String name; //姓名
          
    float hp; //血量
          
    float armor; //护甲
          
    int moveSpeed; //移动速度
      
    public abstract void attack();
      
    public static void main(String[] args) {
          
        ADHero adh=new ADHero();
        //通过打印adh,可以看到adh这个对象属于ADHero类
        adh.attack();
        System.out.println(adh);
          
        Hero h = new Hero(){
            //当场实现attack方法
            public void attack() {
                System.out.println("新的进攻手段");
            }
        };
        h.attack();
        //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名
          
        System.out.println(h);
    }
      
}

本地类 :

本地类可以理解为有名字的匿名类
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方

package charactor;
   
public abstract class Hero {
    String name; //姓名
          
    float hp; //血量
          
    float armor; //护甲
          
    int moveSpeed; //移动速度
      
    public abstract void attack();
      
    public static void main(String[] args) {
          
        //与匿名类的区别在于,本地类有了自定义的类名
        class SomeHero extends Hero{
            public void attack() {
                System.out.println( name+ " 新的进攻手段");
            }
        }
         
        SomeHero h  =new SomeHero();
        h.name ="地卜师";
        h.attack();
    }
      
}

默认方法:

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default

package charactor;
 
public interface Mortal {
    public void die();
 
    default public void revive() {
        System.out.println("本英雄复活了");
    }
}

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

数字与字符串

装箱拆箱:

封装类:

所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类

Number类 :

数字封装类有
Byte,Short,Integer,Long,Float,Double
这些类都是抽象类Number的子类

自动装箱 自动拆箱 :

不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫装箱

不需要调用Integer的intValue方法,通过=就自动转换成int类型,就叫拆箱

package digit;
 
public class TestNumber {
 
    public static void main(String[] args) {
        int i = 5;
 
        //基本类型转换成封装类型
        Integer it = new Integer(i);
         
        //自动转换就叫装箱
        Integer it2 = i;
        
        //封装类型转换成基本类型
        int i2 = it.intValue();
         
        //自动转换就叫拆箱
        int i3 = it;
         
    }
}
int的最大值,最小值 :

int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取

package digit;
  
public class TestNumber {
  
    public static void main(String[] args) {
 
        //int的最大值
        System.out.println(Integer.MAX_VALUE);
        //int的最小值      
        System.out.println(Integer.MIN_VALUE);
          
    }
}

字符串转换:

数字转字符串 :

方法1: 使用String类的静态方法valueOf
方法2: 先把基本类型装箱为对象,然后调用对象的toString

package digit;
  
public class TestNumber {
  
    public static void main(String[] args) {
        int i = 5;
         
        //方法1
        String str = String.valueOf(i);
         
        //方法2
        Integer it = i;
        String str2 = it.toString();
         
    }
}
字符串转数字 :

调用Integer的静态方法parseInt

package digit;
  
public class TestNumber {
  
    public static void main(String[] args) {
 
        String str = "999";
         
        int i= Integer.parseInt(str);
         
        System.out.println(i);
         
    }
}

JAVA中级

上一篇:extra 额外附加


下一篇:如何创建django项目