变量
-
当一个变量被声明在类下面
变量就叫做字段 或者属性、成员变量、Field
那么从第2行这个变量声明的位置开始,整个类都可以访问得到
所以**其作用域就是从其声明的位置开始的整个类 ** -
声明在方法上就叫参数 or 局部变量
其他方法和类中不能访问。 -
final 给予唯一一次赋值,之后不可更改。还可用来修饰类和方法。
操作符
-
如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算。
比如:
int a = 5;
long b = 6;
a+b; -> 结果类型是long 。 -
如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算.
byte a = 1;
byte b= 2;
a+b -> int 类型 -
与C++同样有% ++ –
-
长路与& 短路与&&
长路与 无论第一个表达式的值是true或者false,第二个的值,都会被运算
短路与 只要第一个表达式的值是false的,第二个表达式的值,就不需要进行运算了 -
长路或| 短路或||
长路或 无论第一个表达式的值是true或者false,第二个的值,都会被运算
短路或 只要第一个表达式的值是true的,第二个表达式的值,就不需要进行运算了 -
取非~ 带符号左移<< 带符号右移>> 无符号左移<<< 无符号右移>>>
对二进制而言 -
异或^
相同为零,不同为一。 -
Integer.toBinaryString()一个整数的二进制表达式
String b = (Integer.toBinaryString(i)); -
Scanner
Scanner s = new Scanner(System.in);
float a = s.nextFloat();
.nextFloat()读取浮点数
.nextInt()读取整数
.nextLine()读取字符串
如果在业务上需要读取了整数后,接着读取字符串,那么就应该连续执行两次nextLine(),第一次是取走回车换行,第二次才是读取真正的字符串 。
Scanner s = new Scanner(System.in);
int i = s.nextInt();
System.out.println("读取的整数是"+ i);
String rn = s.nextLine();
String a = s.nextLine();
控制语句
- outloop: 用标签结束外部循环(标签名为自定义的,可以是outloop1、out等等)
eg:
public class HelloWorld {
public static void main(String[] args) {
//打印单数
outloop: //outloop这个标示是可以自定义的比如outloop1,ol2,out5
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println(i+":"+j);
if(0==j%2)
break outloop; //如果是双数,结束外部循环
}
}
}
}
- debug 双击行数设置断点。
断点表示马上就要运行断点行但还为运行。
step over(f6)可以一行行执行代码
数组
-
Math.random(),得到一个0-1之间的随机浮点数
-
int a[] = new int[n]; 申明
如果指定了数组的内容就不能同时设置数组的长度 int[] a = new int[]{100,101,102}; -
增强型for循环
for (int each : values){
System.out.println(each);
}
注:增强型for循环只能用来取值,却不能用来修改数组里的值
eg:
int values [] = new int[]{18,62,68,82,65,9};
//数组中的内容是
for (int each : values) {
System.out.print(each+" ");
}
System.out.println();
int max = -1;
for (int each : values) {
if(each>max)
max = each;
}
System.out.println("最大的一个值是:"+max);
- 复制数组
System.arraycopy(src, srcPos, dest, destPos, length)
src: 源数组
srcPos: 从源数组复制数据的起始位置
dest: 目标数组
destPos: 复制到目标数组的起始位置
length: 复制的长度
int[] b = Arrays.copyOfRange(a, 0, 3);
// copyOfRange(int[] original, int from, int to) // 第一个参数表示源数组
// 第二个参数表示开始位置(取得到)
// 第三个参数表示结束位置(取不到)
-
二维数组
赋值方法:
int[][] c = new int[][]{
{1,2,4},
{4,5},
{6,7,8,9}
}; -
打印数组
content = Arrays.toString(a);
直接把一个数组,转换为字符串,这样方便观察数组的内容 -
数组排序
Arrays.sort(a); -
搜索数组
Arrays.binarySearch(数组,值)
必须先排序。 -
判断是否相同
Arrays.equals(a,b)
比较两个数组的内容是否一样 -
填充
Arrays.fill(数组,值)
使用同一个值,填充整个数组
copyOfRange
数组复制
toString()
转换为字符串
sort
排序
binarySearch
搜索
equals
判断是否相同
fill
填充
需要引用库Arrays
import java.util.Arrays;
类和对象
- 引用
创建:new Hero();
引用:Hero h = new Hero();
多重引用:
//使用一个引用来指向这个对象
Hero h1 = new Hero();
Hero h2 = h1; //h2指向h1所指向的对象
Hero h3 = h1;
Hero h4 = h1;
Hero h5 = h4;
//h1,h2,h3,h4,h5 五个引用,都指向同一
个对象
一个引用同一时间只能指向一个对象
eg:
Hero h1 = new Hero();
Hero h2 = new Hero();
Hero h3;
Hero h4;
h3 = h1;
h4 = h3;
2. 继承
用Weapon继承Item:
public class Item {
String name;
int price;
}
public class Weapon extends Item{
int damage; //攻击力
//他既有Item中的name和price属性,又有新添加的damage属性
}
-
构造方法
方法名和类名一样(包括大小写),但没有返回类型。实例化一个对象的时候,必然调用构造方法。
Hero类的构造方法是:
public Hero(){
}
这种无参的构造方法,如果不写就会默认提供一个。
一旦提供了一个有参的构造方法,同时又没有显式的提供一个无参的构造方法,那么默认的无参的构造方法就没了,这是实例化的时候就要带上参数。
和普通方法一样,构造方法也可以重载。 -
this
4.1
this代表的是当前对象public void showAddressInMemory(){
System.out.println(“打印this看到的虚拟地址:”+this);
}//打印内存中的虚拟地址public static void main(String[] args) {
Hero garen = new Hero();
garen.name = “盖伦”;
//直接打印对象,会显示该对象在内存中的虚拟地址
//格式:Hero@c17164 c17164即虚拟地址,每次执行,得到的地址不一定一样System.out.println("打印对象看到的虚拟地址:"+garen); //调用showAddressInMemory,打印该对象的this,显示相同的虚拟地址 garen.showAddressInMemory(); Hero teemo = new Hero(); teemo.name = "提莫"; System.out.println("打印对象看到的虚拟地址:"+teemo); teemo.showAddressInMemory();
}
4.2
通过this访问属性
//通过this访问属性
public void setName3(String name){
//name代表的是参数name
//this.name代表的是属性name
this.name = name;
}
4.3
如果要在一个构造方法中,调用另一个构造方法,可以使用this()
public Hero(String name){
System.out.println("一个参数的构造方法");
this.name = name;
}//带一个参数的构造方法
//带两个参数的构造方法
public Hero(String name,float hp){
this(name);
System.out.println("两个参数的构造方法");
this.hp = hp;
}
public static void main(String[] args) {
Hero teemo = new Hero("提莫",383);
System.out.println(teemo.name);
}
}
- 如果变量是基本类型,=表赋值;
如果变量是类类型,=表示指向。
**在方法内,无法修改方法外的基本类型参数 **
类类型传参会改变变量值,因为他们指向的是同一个对象
public class Hero {
String name; // 姓名
float hp; // 血量
float armor; // 护甲
int moveSpeed; // 移动速度
public Hero(String name, float hp) {
this.name = name;
this.hp = hp;
}
// 攻击一个英雄,并让他掉damage点血
public void attack(Hero hero, int damage) {
hero.hp = hero.hp - damage;
}
public static void main(String[] args) {
Hero teemo = new Hero("提莫", 383);
Hero garen = new Hero("盖伦", 616);
garen.attack(teemo, 100);//调用attack时,使hero引用指向garen引用。
System.out.println(teemo.hp);
}
}
在方法中,使参数引用指向一个新的对象,在,外面的引用指向的是原来的对象,不发生变化。(局部变量无法影响全局变量)
- 包
把比较接近的类,规划在同一个包下。
使用同一个包下的其他类,可以直接引用;但使用其他包下的类,必须import
package charactor;
//Weapon类在其他包里,使用必须进行import
import property.Weapon;
public class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
//装备一把武器
public void equip(Weapon w){
}
}
- 访问修饰符
成员变量有四种修饰符
private 私有的
package/friendly/default ( 没有修饰符即代表package/friendly/default)
protected 受保护的
public 公共的
自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类
7.1 使用private修饰属性
自身:是可以访问的
同包子类:不能继承
不同包子类:不能继承
同包类:不能访问
其他包类:不能访问
7.2 没有修饰符
自己可以访问
同包子类可以继承
不同包子类不能继承
同包类可以访问
不同包类不能访问
7.3 protected饰符的属性 hp
自己可以访问
同包子类可以继承
不同包子类可以继承
同包类可以访问
不同包类不能访问
7.4 public 公共的
任何地方,都可以访问
红字表示不可以继承、访问
-
属性通常使用private封装起来
-
方法一般使用public用于被调用
-
会被子类继承的方法,通常使用protected
-
package用的不多,一般新手会用package,因为还不知道有修饰符这个东西
-
遵守作用范围最小原则,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。
-
类属性
当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性;当一个属性被声明成类属性,那么所有的对象,都共享一个值。
与对象属性对比:
不同对象的 对象属性 的值都可能不一样。
比如盖伦的hp 和 提莫的hp 是不一样的。
但是所有对象的类属性的值,都是一样的 。
访问类属性有两种方式
- 对象.类属性
teemo.copyright - 类.类属性
Hero.copyright、、建议第二种
如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的。
如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性 。
- 类方法
类方法: 又叫做静态方法
对象方法: 又叫实例方法,非静态方法
访问一个对象方法,必须建立在有一个对象的前提的基础上;访问类方法,不需要对象的存在,直接就访问。
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 = "盖伦";
//必须有一个对象才能调用
garen.die();
Hero teemo = new Hero();
teemo.name = "提莫";
//无需对象,直接通过类调用
Hero.battleWin();
}
}
和访问类属性一样,调用类方法也有两种方式
- 对象.类方法
garen.battleWin(); - 类.类方法
Hero.battleWin();//建议第二种
如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法; 如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法。
-
属性初始化
对象属性初始化:- 声明该属性的时候初始化
- 构造方法中初始化
- 初始化块:
{
maxHP = 200; //初始化块
}
类属性初始化有2种:
-
声明该属性的时候初始化
-
静态初始化块
static{
itemCapacity = 6;//静态初始化块 初始化
}
quiz:对象属性的初始化这三种方式,谁先执行?谁后执行?
ans: 当对一个对象实例化时,首先会加载实例变量,然后再执行初始化代码块,最后执行构造方法。 -
单例模式
单例模式又叫做 Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在。
饿汉式单例模式
GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new 得到新的实例。GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取定义的对象,而且每一次都是获取同一个对象。 从而达到单例的目的。这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例 。
package charactor;
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance = new GiantDragon();
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance(){
return instance;
}
}
package charactor;
public class TestGiantDragon {
public static void main(String[] args) {
//通过new实例化会报错
// GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
//都是同一个对象
System.out.println(g1==g2);
System.out.println(g1==g3);
}
}
懒汉式单例模式
懒汉式单例模式与饿汉式单例模式不同,只有在调用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;
}
}
package charactor;
public class TestGiantDragon {
public static void main(String[] args) {
//通过new实例化会报错
// GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
//都是同一个对象
System.out.println(g1==g2);
System.out.println(g1==g3);
}
}
饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。
懒汉式,是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量(鉴于同学们学习的进度,暂时不对线程的章节做展开)。使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。
看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式。
- 枚举变量
枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量。
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
swtich语句中常用,可以限定死枚举的范围。
public class HelloWorld {
public static void main(String[] args) {
Season season = Season.SPRING;
switch (season) {
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
借助增强型for循环,可以很方便的遍历一个枚举都有哪些常量。
public class HelloWorld {
public static void main(String[] args) {
for (Season s : Season.values()) {
System.out.println(s);
}
}
}
接口与继承
一个类可以有多个子类,一个子类只能有一个父类
- 接口
创建一个接口
File->New->Interface
package charactor;
public interface AD {
//物理伤害
public void physicAttack();
}
实现某个接口,就相当于承诺了某种约定。所以,实现了AD这个接口,就必须提供AD接口中声明的方法physicAttack(),实现在语法上使用关键字 implements。
package charactor;
//同时能进行物理和魔法伤害的英雄
public class ADAPHero extends Hero implements AD,AP{//实现了两个接口
@Override
public void magicAttack() {
System.out.println("进行魔法攻击");
}
@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}
}
快捷方式:Ctrl+1
- 对象转型
package charactor;
public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
ADHero ad = new ADHero();
}
}
在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
对象是有类型的, 是ADHero
引用也是有类型的,是ADHero
通常情况下,引用类型和对象类型是一样的。
2.1 转型
所谓的转型,是指当引用类型和对象类型不一致的时候,才需要进行类型转换。把右边的当做左边来用,看说得通不,如果说得通就转型成功。
2.1.1子类转父类
Hero h = new Hero();
ADHero ad = new ADHero();
h = ad;
右边ad引用所指向的对象的类型是 物理攻击英雄
左边h引用的类型是 普通英雄
把物理攻击英雄 当做 普通英雄,说不说得通? 说得通,就可以转。
所有的子类转换为父类,都是说得通的。
2.1.2 父类转子类
父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。强制转换的意思就是 转换有风险,风险自担。
package charactor;
import charactor1.Support;
public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
Hero h =new Hero();
ADHero ad = new ADHero();
Support s =new Support();
h = ad;
ad = (ADHero) h;
h = s;
ad = (ADHero)h;
}
}
14行: 把ad当做Hero使用,一定可以
转换之后,h引用指向一个ad对象
15行: h引用有可能指向一个ad对象,也有可能指向一个support对象。所以把h引用转换成AD类型的时候,就有可能成功,有可能失败。因此要进行强制转换,换句话说转换后果自负。到底能不能转换成功,要看引用h到底指向的是哪种对象。在这个例子里,h指向的是一个ad对象,所以转换成ADHero类型,是可以的。
16行:把一个support对象当做Hero使用,一定可以。转换之后,h引用指向一个support对象。
17行:这个时候,h指向的是一个support对象,所以转换成ADHero类型,会失败。失败的表现形式是抛出异常ClassCastException,类型转换异常
2.1.3
没有继承关系的两个类,互相转换,一定会失败,虽然ADHero和APHero都继承了Hero,但是彼此没有互相继承关系,转换会失败。
2.1.4 实现类转换成接口(向上转型)
package charactor;
public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
ADHero ad = new ADHero();
AD adi = ad;
}
}
引用ad指向的对象是ADHero类型,这个类型实现了AD接口
10行: 把一个ADHero类型转换为AD接口
从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。
2.1.5 接口转化成实现类(向下转型)
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方法的。
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);
}
}
instanceof Hero 判断一个引用所指向的对象,是否是Hero类型,或者Hero的子类。
- 重写 //override
从父类继承了方法以后,有些方法想要其他效果,就再写一次,调用的时候会执行子类的方法,这就叫重写。
package property;
public class Item { //父类
String name;
int price;
public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果");
}
}
package property;
public class LifePotion extends Item{ //子类
public void effect(){
System.out.println("血瓶使用后,可以回血");
}
public static void main(String[] args) {
LifePotion lp =new LifePotion();
lp.effect();
}
}
- 多态
4.1 操作符的多态
如果+号两侧都是整型,那么+代表 数字相加。
如果+号两侧,任意一个是字符串,那么+代表字符串连接。
4.2 类的多态
都是同一个类型,调用同一个方法,却能呈现不同的状态。
package property;
public class Item {
String name;
int price;
public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果 ");
}
public static void main(String[] args) {
Item i1= new LifePotion();
Item i2 = new MagicPotion();
System.out.print("i1 是Item类型,执行effect打印:");
i1.effect();
System.out.print("i2也是Item类型,执行effect打印:");
i2.effect();
}
}
要实现类的多态,需要如下条件
1.父类(接口)引用指向子类对象
2.调用的方法有重写
使用多态可以通过重写来减少方法。
- 类方法
与重写类似,方法的重写是子类覆盖父类的对象方法。隐藏,就是子类覆盖父类的类方法。
package charactor;
public class Hero {
public String name;
protected float hp;
//类方法,静态方法
//通过类就可以直接调用
public static void battleWin(){
System.out.println("hero battle win");
}
}
package charactor;
public class ADHero extends Hero implements AD{
@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}
//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}
public static void main(String[] args) {
Hero.battleWin();//调用父类的类方法
ADHero.battleWin();//调用子类的类方法
}
}
- super关键字
6.1 在实例化Hero对象的时候,会调用构造方法。
6.2 在实例化子类的对象的时候,会调用父类的构造方法和子类的构造方法,并且父类的构造方法先调用。
6.3 如果父类显示提供两个构造方法,有参和无参
package charactor;
import property.Item;
public class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public void useItem(Item i){
System.out.println("hero use item");
i.effect();
}
public Hero(){
System.out.println("Hero的无参的构造方法 ");
}
public Hero(String name){
System.out.println("Hero的有一个参数的构造方法 ");
this.name = name;
}
public static void main(String[] args) {
new Hero();
}
}
会默认调用无参的构造函数。即使是实例化对象的时候带了参数,也会默认调用无参的构造函数。
6.4 super关键字的作用
1、super表示超(父)类的意思,this表示对象本身。
2、super可用于访问父类被子类隐藏或着覆盖的方法和属性,使用形式为super.方法(属性。
3、在类的继承中,子类的构造方法中默认会有super()语句存在(默认隐藏),相当于执行父类的相应构造方法中的语句,若显式使用则必须位于类的第一行 。
4、对于父类有参的构造方法,super不能省略,否则无法访问父类的有参构造方法,使用形式为super(xx,xx…)。
class Person {
protected void print() {
System.out.println("The print() in class Person.");
}
}
public class DemoSuper extends Person {
public DemoSuper(){
super(); //调用父类的构造方法,而且放第一行,如果不写,系统自动加
}
public void print() {
System.out.println("The print() in class DemoSuper.");
super.print();// 调用父类的方法
}
public static void main(String[] args) {
DemoSuper ds = new DemoSuper();
ds.print();
}
}
- object类
6.1 object类是所有类的父类
声明一个类的时候,默认是继承了Object
public class Hero extends Object
6.2 Object类提供一个toString方法,所以所有的类都有toString方法。toString()的意思是返回当前对象的字符串表达。通过 System.out.println 打印对象就是打印该对象的toString()返回值
package charactor;
public class Hero {
public String name;
protected float hp;
public String toString(){
return name;
}
public static void main(String[] args) {
Hero h = new Hero();
h.name = "盖伦";
System.out.println(h.toString());
//直接打印对象就是打印该对象的toString()返回值
System.out.println(h);
}
}
6.3 finalize()//垃圾回收
当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件
当它被垃圾回收的时候,它的finalize() 方法就会被调用。
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
package charactor;
public class Hero {
public String name;
protected float hp;
public String toString(){
return name;
}
public void finalize(){
System.out.println("这个英雄正在被回收");
}
public static void main(String[] args) {
//只有一引用
Hero h;
for (int i = 0; i < 100000; i++) {
//不断生成新的对象
//每创建一个对象,前一个对象,就没有引用指向了
//那些对象,就满足垃圾回收的条件
//当,垃圾堆积的比较多的时候,就会触发垃圾回收
//一旦这个对象被回收,它的finalize()方法就会被调用
h = new Hero();
}
}
}
6.4 equals()和 ==
equals() 用于判断两个对象的内容是否相同
1、“==”比较两个变量本身的值,即两个对象在内存中的首地址,通俗的讲,用于判断两个引用是否指向同一个对象。
2、“equals()”比较字符串中所包含的内容是否相同。
String s1,s2,s3 = "abc", s4 ="abc" ;
s1 = new String("abc");
s2 = new String("abc");
s1==s2 是 false //两个变量的内存地址不一样,也就是说它们指向的对象不 一样,
s1.equals(s2) 是 true //两个变量的所包含的内容是abc,故相等。
6.5 hashCode()
hashCode方法返回一个对象的哈希值
6.6线程同步相关方法
wait()
notify()
notifyAll()
6.6 getClass()
getClass()会返回一个对象的类对象
- final
当类被修饰成final时,表示类不能够被继承,其子类会出现编译错误。
7.1 final修饰类
package charactor;
public final class Hero extends Object {
String name; //姓名
float hp; //血量
}
7.2 final修饰方法
Hero的useItem方法被修饰成final,那么该方法在ADHero中,不能够被重写
package charactor;
import property.Item;
public class Hero extends Object {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public final void useItem(Item i){
System.out.println("hero use item");
i.effect();
}
public Hero(){
System.out.println("Hero的无参的构造方法 ");
}
public Hero(String name){
System.out.println("Hero的有一个参数的构造方法 ");
this.name = name;
}
public static void main(String[] args) {
new Hero();
}
}
7.3 final修饰基本类型变量
package charactor;
public class Hero extends Object {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public static void main(String[] args) {
final int hp;
hp = 5;
hp = 6;
}
}
7.4 final修饰引用
final修饰引用
h引用被修饰成final,表示该引用只有1次指向对象的机会。
final Hero h;
h =new Hero();
h =new Hero();//编译出错
7.5 final定义常量
常量指的是可以公开,直接访问,不会变化的值 .
public static final int itemTotalNumber = 6;//物品栏的数量
String 类有final修饰,不能被继承。
- 抽象类
在类中声明一个方法,这个方法没有实现体,是一个“空”方法
这样的方法就叫抽象方法,使用修饰符“abstract”
当一个类有抽象方法的时候,该类必须被声明为抽象类
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();
}
为Hero增加一个抽象方法 attack,并且把Hero声明为abstract的。
APHero,ADHero,ADAPHero是Hero的子类,继承了Hero的属性和方法。
但是各自的攻击手段是不一样的,所以继承Hero类后,这些子类就必须提供不一样的attack方法实现。
8.1 抽象类可以没有抽象方法
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();//提示错误
}
}
8.2 抽象类和接口的区别
1、子类只能继承一个抽象类,不能继承多个;子类可以实现多个接口。
2、
抽象类可以定义
public,protected,package,private
静态和非静态属性
final和非final属性
但是接口中声明的属性,只能是
public static final的
即便没有显式的声明
抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法
- 内部类
内部类分为四种:
非静态内部类
静态内部类
匿名类
本地类
9.1 非静态内部类
非静态内部类可以直接在一个类里面定义。
战斗成绩只有在一个英雄对象存在的时候才有意义
所以实例化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();
}
}
9.2 静态内部类
在一个类里面声明一个静态内部类
比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。
与非静态内部类不同,静态内部类水晶类的实例化不需要一个外部类的实例为基础,可以直接实例化
语法:new 外部类.静态内部类();
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别
static class EnemyCrystal{
int hp=5000;
}
public static void main(String[] args) {
//实例化静态内部类
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
9.3 匿名类
匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
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);
}
}
9.4 本地方法
本地类可以理解为有名字的匿名类
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,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();
}
}
9.5 在匿名类中使用外部的局部变量
在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final。
- 默认方法
默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法
Mortal 这个接口,增加了一个默认方法 == revive,这个方法有实现体,并且被声明为了default==
package charactor;
public interface Mortal {
public void die();
default public void revive() {
System.out.println("本英雄复活了");
}
}
假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法
通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类 //降低开发和维护效率
为AD接口增加一个默认方法 attack()
为AP接口也增加一个默认方法 attack()
问: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?
ans:使用AD.super.attack(),即可调用默认的attack()
- UML图
UML-Unified Module Language
统一建模语言,可以很方便的用于描述类的属性,方法,以及类和类之间的关系
带箭头的实线,表示 Spider,Cat, Fish都继承于Animal这个父类.
注: 模糊是表示,此时不需要关注模糊的那部分内容。
表示 Fish实现了 Pet这个接口。
数字与字符串
- 装箱与拆箱
1.1 封装类
所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类
1.2 Number类
数字封装类有
Byte,Short,Integer,Long,Float,Double
这些类都是抽象类Number的子类
1.3 封装类与基本类互转
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
}
}
1.4 自动装箱
不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫装箱
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//自动转换就叫装箱
Integer it2 = i;
}
}
1.5 自动拆箱
不需要调用Integer的intValue方法,通过=就自动转换成int类型,就叫拆箱
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
//自动转换就叫拆箱
int i3 = it;
}
}
1.6 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);
}
}
- 字符串转换
2.1数字转字符串
方法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();
}
}
2.2 字符串转数字
调用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);
}
}
- 数学方法
3.1 四舍五入, 随机数,开方,次方,π,自然常数
package digit;
public class TestNumber {
public static void main(String[] args) {
float f1 = 5.4f;
float f2 = 5.5f;
//5.4四舍五入即5
System.out.println(Math.round(f1));
//5.5四舍五入即6
System.out.println(Math.round(f2));
//得到一个0-1之间的随机浮点数(取不到1)
System.out.println(Math.random());
//得到一个0-10之间的随机整数 (取不到10)
System.out.println((int)( Math.random()*10));
//开方
System.out.println(Math.sqrt(9));
//次方(2的4次方)
System.out.println(Math.pow(2,4));
//π
System.out.println(Math.PI);
//自然常数
System.out.println(Math.E);
}
}
- 格式化输出
如果不使用格式化输出,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐
使用格式化输出,就可以简洁明了
%s 表示字符串
%d 表示数字
%n 表示换行
package digit;
public class TestNumber {
public static void main(String[] args) {
String name ="盖伦";
int kill = 8;
String title="超神";
//直接使用+进行字符串连接,编码感觉会比较繁琐,并且维护性差,易读性差
String sentence = name+ " 在进行了连续 " + kill + " 次击杀后,获得了 " + title +" 的称号";
System.out.println(sentence);
//使用格式化输出
//%s表示字符串,%d表示数字,%n表示换行
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
System.out.printf(sentenceFormat,name,kill,title);//format能够达到一模一样的效果
}
}
4.1 换行符
(1)在DOS和Windows中,每行结尾是 “\r\n”;
(2)Linux系统里,每行结尾只有 “\n”;
(3)Mac系统里,每行结尾是只有 “\r”。
为了使得同一个java程序的换行符在所有的操作系统中都有一样的表现,使用%n,就可以做到平台无关的换行
4.2 总长度,左对齐,补0,千位分隔符,小数点位数,本地化表达
package digit;
import java.util.Locale;
public class TestNumber {
public static void main(String[] args) {
int year = 2020;
//总长度,左对齐,补0,千位分隔符,小数点位数,本地化表达
//直接打印数字
System.out.format("%d%n",year);
//总长度是8,默认右对齐
System.out.format("%8d%n",year);
//总长度是8,左对齐
System.out.format("%-8d%n",year);
//总长度是8,不够补0
System.out.format("%08d%n",year);
//千位分隔符
System.out.format("%,8d%n",year*10000);
//小数点位数
System.out.format("%.2f%n",Math.PI);
//不同国家的千位分隔符
System.out.format(Locale.FRANCE,"%,.2f%n",Math.PI*10000);
System.out.format(Locale.US,"%,.2f%n",Math.PI*10000);
System.out.format(Locale.UK,"%,.2f%n",Math.PI*10000);
}
}
5. 字符
5.1 保留一个字符时用char
5.2 char对应的封装类是Character
Character c = c1;//自动装箱
c1 = c;//自动拆箱
5.3 Character的常见方法
package character;
public class TestChar {
public static void main(String[] args) {
System.out.println(Character.isLetter('a'));//判断是否为字母
System.out.println(Character.isDigit('a')); //判断是否为数字
System.out.println(Character.isWhitespace(' ')); //是否是空白
System.out.println(Character.isUpperCase('a')); //是否是大写
System.out.println(Character.isLowerCase('a')); //是否是小写
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
String a = 'a'; //不能够直接把一个字符转换成字符串
String a2 = Character.toString('a'); //转换为字符串
}
}
5.4常见歧义
package character;
public class TestChar {
public static void main(String[] args) {
System.out.println("使用空格无法达到对齐的效果");
System.out.println("abc def");
System.out.println("ab def");
System.out.println("a def");
System.out.println("使用\\t制表符可以达到对齐的效果");
System.out.println("abc\tdef");
System.out.println("ab\tdef");
System.out.println("a\tdef");
System.out.println("一个\\t制表符长度是8");
System.out.println("12345678def");
System.out.println("换行符 \\n");
System.out.println("abc\ndef");
System.out.println("单引号 \\'");
System.out.println("abc\'def");
System.out.println("双引号 \\\"");
System.out.println("abc\"def");
System.out.println("反斜杠本身 \\");
System.out.println("abc\\def");
}
}
- 字符串
6.1 创建字符串
常见创建字符串手段:
1、 每当有一个字面值出现的时候,虚拟机就会创建一个字符串
2、调用String的构造方法创建一个字符串对象
3.、 通过+加号进行字符串拼接也会创建新的字符串对象
String garen ="盖伦"; //字面值,虚拟机碰到字面值就会创建一个字符串对象
String hero = new String(cs);// 通过字符数组创建一个字符串对象
String hero3 = garen + teemo;// 通过+加号进行字符串拼接
6.2 String 被修饰为final,所以是不能被继承的
6.3 immutable
immutable 是指不可改变的
比如创建了一个字符串对象
String garen =“盖伦”;
不可改变的具体含义是指:
不能增加长度
不能减少长度
不能插入字符
不能删除字符
不能修改字符
一旦创建好这个字符串,里面的内容 永远 不能改变
String 的表现就像是一个常量
6.4 字符串长度
length方法可以返回当前字符串的长度
可以有长度为0的字符串,即空字符串
6.5 字符串格式化输出
String类也有format方法,可以得到格式化后的字符串。
6.6
创建一个长度是5的随机字符串的方法
String pool = "";
for (short i = '0'; i <= '9'; i++) {
pool+=(char)i;
}
for (short i = 'a'; i <= 'z'; i++) {
pool+=(char)i;
}
for (short i = 'A'; i <= 'Z'; i++) {
pool+=(char)i;
}
char cs2[] = new char[5];
for (int i = 0; i < cs2.length; i++) {
int index = (int) (Math.random()*pool.length());
cs2[i] = pool.charAt( index );
}
String result2 = new String(cs2);
System.out.println(result2);
- 操作字符串
7.1 charAt(int index)获取指定位置的字符
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
char c = sentence.charAt(0);
System.out.println(c);
}
}
7.2 toCharArray(),获取对应的字符数组
7.3 subString,截取子字符串
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//截取从第3个开始的字符串 (基0)
String subString1 = sentence.substring(3);
System.out.println(subString1);
//截取从第3个开始的字符串 (基0)
//到5-1的位置的字符串
//左闭右开
String subString2 = sentence.substring(3,5);
System.out.println(subString2);
}
}
7.4 split,根据分隔符进行分隔
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//根据,进行分割,得到3个子字符串
String subSentences[] = sentence.split(",");
for (String sub : subSentences) { System.out.println(sub);
}
}
}
7.5 trim去掉首位空格
package character;
public class TestString {
public static void main(String[] args) {
String sentence = " 盖伦,在进行了连续8次击杀后,获得了 超神 的称号 ";
System.out.println(sentence);
//去掉首尾空格 System.out.println(sentence.trim());
}
}
7.6
toLowerCase 全部变成小写
toUpperCase 全部变成大写
7.7 定位
indexOf 判断字符或者子字符串出现的位置
contains 是否包含子字符串
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
System.out.println(sentence.indexOf('8')); //字符第一次出现的位置
System.out.println(sentence.indexOf("超神")); //字符串第一次出现的位置
System.out.println(sentence.lastIndexOf("了")); //字符串最后出现的位置
System.out.println(sentence.indexOf(',',5)); //从位置5开始,出现的第一次,的位置
System.out.println(sentence.contains("击杀")); //是否包含字符串"击杀"
}
}
7.8 替换
replaceAll 替换所有的
replaceFirst 只替换第一个
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
String temp = sentence.replaceAll("击杀", "被击杀"); //替换所有的
temp = temp.replaceAll("超神", "超鬼");
System.out.println(temp);
temp = sentence.replaceFirst(",","");//只替换第一个
System.out.println(temp);
}
}
- 比较字符串
1、equal方法 //内容比较
2、== //对象比较
一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
所以在第6行会创建了一个新的字符串"the light"
但是在第7行,编译器发现已经存在现成的"the light",那么就直接拿来使用,而没有进行重复创建
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String str3 = "the light";
System.out.println( str1 == str3);
}
}
8.1
startsWith //以…开始
endsWith //以…结束
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String start = "the";
String end = "Ight"; System.out.println(str1.startsWith(start));//以...开始 System.out.println(str1.endsWith(end));//以...结束
}
}
- StringBuffer
9.1 追加 删除 插入 反转
append追加
delete 删除
insert 插入
reverse 反转 //首尾
9.2 长度 容量
为什么StringBuffer可以变长?
和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度
比如说new StringBuffer(“the”),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the";
StringBuffer sb = new StringBuffer(str1);
System.out.println(sb.length()); //内容长度
System.out.println(sb.capacity());//总空间
}
}
length: “the”的长度 3
capacity: 分配的总空间 19
注: 19这个数量,不同的JDK数量是不一样的
StringBuffer 的运行效率比String高