Java教程

JAVA学习记录

本文主要是介绍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");
}

变量

基本变量类型:

整型:
类型长度
byte8位
short16位
int32位
long64位
字符型:
类型长度
char16位
浮点型:
类型长度
float32位
double64位
布尔型:
类型长度
boolean1位
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中级

这篇关于JAVA学习记录的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!