Java基本语法

目录

一、Java基础

1.1 面向对象

1、类和对象

2、属性

3、方法

 1.2 变量

1、基本数据类型

2、字面值

3、类型转换

4、命名规则

 5、作用域

6、变量final

7、表达式

8、块

1.3 操作符

1、算数操作符

2、关系运算符

3、逻辑运算符

4、位操作符

5、赋值操作符

6、三元操作符

7、操作符Scanner

1.4 控制流程

1、if

2、switch

3、while

4、for

5、continue

6、break 

7、结束外部循环

1.5 数组

1、创建数组

2、初始化数组

3、排序

4、增强型for循环

5、复制数组

6、二维数组

7、数组Arrays

  1.6 类和对象

1、引用

2、继承

3、方法重载

4、构造方法

5、this

6、传参

7、包

8、访问修饰符

9、类属性

10、类方法

11、属性初始化

12、单例模式

13、枚举类型

1.7 接口和继承

1、接口

2、对象转型

3、重写

4、多态

5、隐藏

6、super

7、Object类

8、final

9、抽象类

10、内部类

11、默认方法

1.8 数字与字符串

1、装箱拆箱

2、字符串转换

3、数学方法

4、格式化输出

 5、字符

 6、字符串

7、操纵字符串

8、比较字符串

9、StringBuffer

10、MyStringBuffer

 1.9 日期

1、Date

 2、日期格式化

3、Calendar


一、Java基础

1.1 面向对象

1、类和对象

Hero是一个类,类就像是一个模板,根据模板可以创建一个个具体的英雄,具体的英雄就叫做对象。

类的首字母大写。

2、属性

属性的类别可以是基本类型,也可以是类的类型。

属性的名称一般都是小写。如果有多个单词组成,后面的单词首字母大写。

3、方法

方法就是英雄可以做什么。

有的方法是有返回类型的,有的方法不需要返回值,需设置为void。

方法是一个类的动作行为,所以一般都是以动词开头的,如果有多个单词后面的每个单词首字母都要大写。

//设计英雄这个类+创建具体的英雄

public class Hero {  //Hero是一个类,类一般都首字母大写

    String name; //姓名 //name是一个属性,属性的类型可以是基本类型,也可以是类类型

    float hp; //血量

    float armor; //护甲

    int moveSpeed; //移动速度

    //坑队友
    void keng(){
    	System.out.println("坑队友!"); //keng就是一种方法
    }

    //获取护甲值
    float getArmor(){
    	return armor;
    }
    
    //增加移动速度
    void addSpeed(int speed){
    	//在原来的基础上增加移动速度
    	moveSpeed = moveSpeed + speed; //int speed就是方法的参数
    }

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

}

 1.2 变量

1、基本数据类型

变量是用来命名一个数据的标识符。

Java的八种基本变量类型:整型(4种)、字符型(1种)、浮点型(2种)、布尔型(1种)

整型:用来存放整数。

字符型(char):用来存放一个字符,用单引号表示。(双引号表示字符串)

 浮点数型:默认的小数值是double类型的。float型应该在数字后面加f。

 

布尔型:用来表示真假,长度为1。不能直接是用0和1进行赋值。

String类型:并不是基本数据类型。是Immutable的,一旦创建就不能被改变。 

2、字面值

给基本类型的变量赋值的方式叫做字面值。

整数字面值:以L结尾的时候,一个整数的字面值就是long型,否则就是int型。

byte、short、long、int的值都可以通过int类型的字面值来创建。

整数的字面值可以用如下的四种进制来表示:

十进制:基10,包含0-9的数字。

十六进制:基16,包含从0-9的数字,和从A-F的字母。

八进制:基8,包含从0-7的数字

二进制:基2,包含0和1

public class HelloWorld {
    public static void main(String[] args) {
        long val = 26L; //以L结尾的字面值表示long型
        int decVal = 26; //默认就是int型
        int hexVal = 0x1a; //16进制
        int oxVal = 032; //8进制
        int binVal = 0b11010; //2进制
        System.out.println(oxVal);
    }
}

浮点数字面值:当以f或F结尾的时候就表示一个float型的浮点数,否则就是double型(以d或D结尾,写不写都可)。

浮点数还可以以E或e表示(科学计数法)

public class HelloWorld {

	public static void main(String[] args) {
		float f1 = 123.4F;// 以F结尾的字面值表示float类型
		double d1 = 123.4;// 默认就是double类型
		double d2 = 1.234e2;// 科学计数法表示double
	}
}

字符和字符串字面值: 字符的字面值放在单引号中,字符串的字面值放在双引号。

\表示转义,比如需要表示制表符,回车换行,双引号就需要用\t \r \n \'

public class HelloWorld {

	public static void main(String[] args) {
		String name = "盖伦";
		char a= 'c';

		//以下是转义字符
		char tab = '\t'; //制表符
		char carriageReturn = '\r'; //回车
		char newLine = '\n'; //换行
		char doubleQuote = '\"'; //双引号
		char singleQuote = '\''; //单引号
		char backslash = '\\'; //反斜杠
		
	}
}

3、类型转换

不同类型之间的数据可以相互转换。

从小到大自动转,从大到小强制转。虽然char和short都是16位,但是仍需要强制转换。

public class HelloWorld {

	public static void main(String[] args) {

		char c = 'A';
		short s = 80;
		
		//虽然short和char都是16位的,长度是一样的
		//但是彼此之间,依然需要进行强制转换
		c = (char) s;
		//直接进行转换,会出现编译错误
		s = c;
		
	}
}

低精度转高精度:低精度直接赋值给高精度

高精度转低精度:强制转换,有时数据会损失。

public class HelloWorld {
 
    public static void main(String[] args) {
 
        byte b = 5;
        int i1 = 10;
        int i2 = 300;
         
        b = (byte) i1;
        //因为i1的值是在byte范围之内,所以即便进行强制转换
        //最后得到的值,也是10
        System.out.println(b);
         
        //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
        //i2的值是300,其对应的二进制数是 100101100
        //按照byte的长度8位进行截取后,其值为 00101100 即44
        b =(byte) i2;
        System.out.println(b);
        
        //查看一个整数对应的二进制的方法:
        System.out.println(Integer.toBinaryString(i2));
        
    }
}

4、命名规则

变量命名只能使用字母、数字和 $ _。变量的第一个字符只能使用字母 $ _,不能使用数字。

在命名时尽量使用完整的单词进行命名,而不是使用缩写。

不能只使用关键字,但是可以包含关键字。(关键字:被java赋予特殊含义的单词)

中文也是可以用来命名的,但是在实际工作中别这么干。

int a= 5;
int a_12= 5;
int $a43= 5;

int a434= 5;
//第一个是数字,是不行的
int 34a= 5;



public class Hero {
     //使用完整单词命名,易于理解
    String name; 
     
    float hp; 
     
    float armor; 
     
    int moveSpeed; 
}

关键字列表:

 5、作用域

 变量处于不同的位置,就有不同的 名称。分别是字段,属性,参数和局部变量。不同名称的变量,其作用域是不一样的。

字段,属性,Field:

当一个变量被声明在类的下面,变量就叫做字段或者属性、成员变量、Field。其作用域激素hi从其生命位置开始的整个类。

public class HelloWorld {
	int i = 1;
	int j = i;  //其他的属性可以访问i
	public void method1(){
		System.out.println(i); //方法1里可以访问i
	}
	public void method2(){
		System.out.println(i); //方法2里可以访问i
	}
}

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

public class HelloWorld {

	public void method1(int i){ //参数i的作用域即方法method1
		System.out.println(i);
	}
	public void method2(){ 

		System.out.println(i); //method2 不能访问参数i
	}
	
	int j = i;  //类里面也不能访问参数i
}

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

public class HelloWorld {

	public void method1() {
		int i  = 5;  //其作用范围是从声明的第4行,到其所处于的块结束12行位置
		System.out.println(i);
		{            //子块
			System.out.println(i); //可以访问i
			int j = 6;
			System.out.println(j); //可以访问j
		}
		System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
	}

}

6、变量final

当一个变量被final修饰的时候,该变量只有一次赋值的机会。final除了修饰变量,还可以修饰类、修饰方法。

7、表达式

以分号结尾的一段代码,即为一个表达式。分号也是一个完整的表达式。

8、块

从{开始到对应的}结束,即为一个块。


1.3 操作符

1、算数操作符

基本算数操作符:+ - * /

任意运算单元的长度超过int,那么运算结果就按照最长的长度计算。

任意运算单元的长度都不超过int,那么运算结果就按照int计算。

%取余/取模  ++ -- 自增自减

i++; 先取值,再运算

++i;先运算,再取值

2、关系运算符

关系运算符:比较两个变量之间的关系。> < == >= <= !=

3、逻辑运算符

&长路与两边运算单元都是布尔值,都为true,才为真;任意为false,就为假。长路与两侧,都会被运算。
&&短路与两边运算单元都是布尔值,都为true,才为真;任意为false,就为假。短路与只要第一个是false,第二个就不会被运算。
|长路或两边运算单元都是布尔值,都为false,才为假;任意为true,就为真。长路与两侧,都会被运算。
||短路或两边运算单元都是布尔值,都为false,才为假;任意为true,就为真。短路或只要第一个是true,第二个就不会被运算。
取反真变为假,假变为真。
^异或不同,返回真;相同,返回假。

4、位操作符

1、一个整数的二进制表达

位操作都是对于二进制而言的。通过Integer.toBinaryString()方法,将一个十进制整数转换为一个二进制字符串。

public class HelloWorld {
    public static void main(String[] args) {
        int i = 5;
        String b = (Integer.toBinaryString(i)); // 5的二进制的表达101
        System.out.println(i+" 的二进制表达是: "+b);
    }
}

2、位或 |

5的二进制是101
6的二进制是110
所以 5|6 对每一位进行或运算,得到 111->7

public class HelloWorld {
	public static void main(String[] args) {
		
		int i  =5;
		int j = 6;
		
		System.out.println(Integer.toBinaryString(i)); //5的二进制是101
		
		System.out.println(Integer.toBinaryString(j)); //6的二进制是110
		
		System.out.println(i|j); //所以 5|6 对每一位进行或运算,得到 111->7

	}
}

3、位与 &

5的二进制是101
6的二进制是110
所以 5&6 对每一位进行与运算,得到 100->4

public class HelloWorld {
	public static void main(String[] args) {
		
		int i  =5;
		int j = 6;
		
		System.out.println(Integer.toBinaryString(i)); //5的二进制是101
		
		System.out.println(Integer.toBinaryString(j)); //6的二进制是110
		
		System.out.println(i&j); //所以 5&6 对每一位进行与运算,得到 100->4

	}
}

4、异或 ^

5的二进制是101
6的二进制是110
所以 5^6 对每一位进行异或运算,得到 011->3

一些特别情况:
任何数和自己进行异或 都等于 0
任何数和0 进行异或 都等于自己

public class HelloWorld {
	public static void main(String[] args) {
		int i  =5;
		int j = 6;
		System.out.println(Integer.toBinaryString(i)); //5的二进制是 101
		System.out.println(Integer.toBinaryString(j)); //6的二进制是110
		System.out.println(i^j); //所以 5^6 对每一位进行或运算,得到 011->3
		
		System.out.println(i^0);
		System.out.println(i^i);
	}
}

5、取非 ~

5 的二进制是 00000101
所以取反即为 11111010
这个二进制换算成十进制即为-6

public class HelloWorld {
	public static void main(String[] args) {
		byte i  =5;
		
		System.out.println(Integer.toBinaryString(i)); //5的二进制是00000101,所以取非即为11111010,即为-6
		
		System.out.println(~i);
		
	}
	
}

6、<< 左移  >>右移

左移:根据一个整数的二进制表达,将其每一位都向左移动,最右边一位补0
右移:根据一个整数的二进制表达,将其每一位都向右移动

public class HelloWorld {
	public static void main(String[] args) {
		byte i  =6;
		
		//6的二进制是110
		System.out.println(Integer.toBinaryString(i)); 
		//6向左移1位后,变成1100,对应的10进制是12
		System.out.println(i<<1);
		//6向右移1位后,变成11,对应的10进制是3
		System.out.println(i>>1);
	}
	
}

7、带符号右移与无符号右移

带符号右移 >>
对于正数, 带符号右移 >> 会把所有的位右移,并在最前面补0
对于负数, 带符号右移 >> 会把所有的位右移,并在最前面补1
无符号右移>>>
如果是一个负数,那么对应的二进制的第一位是1
无符号右移>>>会把第一位的1也向右移动,导致移动后,第一位变成0
这样就会使得负数在无符号右移后,得到一个正数

简单的说:
带符号右移 >> 移动后正的还是正的,负的还是负的,符号不变
无符号右移>>>移动后,变正的了

public class HelloWorld {
	public static void main(String[] args) {
		int i  =-10;
		
		//-10的二进制是11111111111111111111111111110110
		//第一位是1,即符号位,代表这是一个负数
		System.out.println(Integer.toBinaryString(i));
		
		//对于正数, 带符号右移 >> 会把所有的位右移,并在最前面补0
		//对于负数, 带符号右移 >> 会把所有的位右移,并在最前面补1
		
		//-10带符号右移1位,移动后前面补齐1
		//得到11111111111111111111111111111011
		//因为第一位是1,所以依然是一个负数,对应的十进制是-5
		int j = i>>1;
		System.out.println(Integer.toBinaryString(j));
		System.out.println(j);
		
		//-10无符号向右移1位,符号位也会向右移,第一位就变成了0
		//得到01111111111111111111111111111011,对应的十进制是2147483643
		int k = i>>>1;
		System.out.println(Integer.toBinaryString(k));		
		
		System.out.println(k);
	}
	
}

5、赋值操作符

=赋值操作
+=  -=  *=  /= %= &= |= ^=  <<= >>= >>>=对本身进行运算,并赋值

6、三元操作符

表达式?值1:值2

如果表达式为真,返回值1;如果表达式为假,返回值2。

7、操作符Scanner

System.out.println("")向控制台输出数据;从控制台输入数据,需要用到Scanner类

1、使用Scanner读取整数

使用Scanner类,需要在前面加上 import java.util.Scanner;

import java.util.Scanner;

public class HelloWorld {
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		int a = s.nextInt();
		System.out.println("第一个整数:"+a);
		int b = s.nextInt();
		System.out.println("第二个整数:"+b);
	}
}

2、使用Scanner读取浮点数

import java.util.Scanner;
 
public class HelloWorld {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        float a = s.nextFloat();
        System.out.println("读取的浮点数的值是:"+a);

    }
}

3、使用Scanner读取字符串

import java.util.Scanner;
 
public class HelloWorld {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        String a = s.nextLine();
        System.out.println("读取的字符串是:"+a);
    }
}

4、读取整数之后,接着读取字符串

需要注意的是,如果在通过nextInt()读取了整数后,再接着读取字符串,读出来的是回车换行:"\r\n",因为nextInt仅仅读取数字信息,而不会读取回车换行"\r\n".

所以,如果在业务上需要读取了整数后,接着读取字符串,那么就应该连续执行两次nextLine(),第一次是取走回车换行,第二次才是读取真正的字符串

import java.util.Scanner;
  
public class HelloWorld {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int i = s.nextInt();
        System.out.println("读取的整数是"+ i);
        String rn = s.nextLine();
        String a = s.nextLine();
        System.out.println("读取的字符串是:"+a);
    }
}

1.4 控制流程

1、if

1、if

if(表达式1){

表达式2;

}

public class HelloWorld {
	public static void main(String[] args) {
		
		boolean b = true;
		//如果成立就打印yes
		if(b){
			System.out.println("yes");
		}
		
	}
}

2、多表达式与一个表达式

public class HelloWorld {
	public static void main(String[] args) {
		
		boolean b = false;
		//如果有多个表达式,必须用大括弧包括起来
		if(b){
			System.out.println("yes1");
			System.out.println("yes2");
			System.out.println("yes3");
		}
		
		//否则表达式2 3 无论b是否为true都会执行
		
		if(b)
			System.out.println("yes1");
			System.out.println("yes2");
			System.out.println("yes3");
			
		//如果只有一个表达式可以不用写括弧,看上去会简约一些
		if(b){
			System.out.println("yes1");
		}
		
		if(b)
			System.out.println("yes1");
		
	}
}

3、if 使用过程中可能遇到的坑

在第6行,if后面有一个分号; 而分号也是一个完整的表达式
如果b为true,会执行这个分号,然后打印yes
如果b为false,不会执行这个分号,然后打印yes
这样,看上去无论如何都会打印yes

public class HelloWorld {
	public static void main(String[] args) {

		boolean b = false;

		if (b);
			System.out.println("yes");

	}
}

4、if else

else表示不成立的情况

public class HelloWorld {
	public static void main(String[] args) {

		boolean b = false;

		if (b)
			System.out.println("yes");
		else
			System.out.println("no");

	}
}

5、else if 是多条件判断

public class HelloWorld {
	public static void main(String[] args) {

		//如果只使用 if,会执行4次判断
		int i = 2;
		if (i==1)
			System.out.println(1);
		if (i==2)
			System.out.println(2);
		if (i==3)
			System.out.println(3);
		if (i==4)
			System.out.println(4);
		
		//如果使用else if, 一旦在18行,判断成立, 20行和22行的判断就不会执行了,节约了运算资源
		if (i==1)
			System.out.println(1);
		else if (i==2)
			System.out.println(2);
		else if (i==3)
			System.out.println(3);
		else if (i==4)
			System.out.println(4);		
		
	}
}

2、switch

switch 语句相当于 if else的另一种表达方式

switch可以使用byte,short,int,char,String,enum

注: 每个表达式结束,都应该有一个break;
注: String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数
注: enum是枚举类型,在枚举章节有详细讲解

public class HelloWorld {
	public static void main(String[] args) {
		
		//如果使用if else
		int day = 5;
		if (day==1)
			System.out.println("星期一");
			 
		else if (day==2)
			System.out.println("星期二");
		else if (day==3)
			System.out.println("星期三");
		else if (day==4)
			System.out.println("星期四");
		else if (day==5)
			System.out.println("星期五");
		else if (day==6)
			System.out.println("星期六");
		else if (day==7)
			System.out.println("星期天");
		else
			System.out.println("这个是什么鬼?");
		
		//如果使用switch
		switch(day){
			case 1:
				System.out.println("星期一");
				break;
			case 2:
				System.out.println("星期二");
				break;
			case 3:
				System.out.println("星期三");
				break;
			case 4:
				System.out.println("星期四");
				break;
			case 5:
				System.out.println("星期五");
				break;
			case 6:
				System.out.println("星期六");
				break;
			case 7:
				System.out.println("星期天");
				break;
			default:
				System.out.println("这个是什么鬼?");
		}
		
	}
}

3、while

1、while

只要while中的表达式成立,就会不断地循环执行

public class HelloWorld {
	public static void main(String[] args) {
		
        //打印0到4		
		int i = 0;
		while(i<5){
			System.out.println(i);
			i++;
		}
	}
}

2、do while

do{

}while(条件)

与while的区别:

无论是否成立,先执行一次,再进行判断

public class HelloWorld {
	public static void main(String[] args) {
		
        //打印0到4
		//与while的区别是,无论是否成立,先执行一次,再进行判断
		int i = 0;
		do{
			System.out.println(i);
			i++;			
		} while(i<5);
		
	}
}

4、for

public class HelloWorld {
    public static void main(String[] args) {
          
        //使用while打印0到4     
        int i = 0;
        while(i<5){
            System.out.println("while循环输出的"+i);
            i++;
        }
         
        //使用for打印0到4     
        for (int j = 0; j < 5; j++) {
            System.out.println("for  循环输出的"+j);
        }
    }
}

5、continue

public class HelloWorld {
    public static void main(String[] args) {
         
        //打印单数     
        for (int j = 0; j < 10; j++) {
        	if(0==j%2)  
        		continue; //如果是双数,后面的代码不执行,直接进行下一次循环
        	
        	System.out.println(j);
		}
    }
}

6、break 

public class HelloWorld {
    public static void main(String[] args) {
         
        //打印单数     
        for (int j = 0; j < 10; j++) {
        	if(0==j%2)  
        		break; //如果是双数,直接结束循环
        	
        	System.out.println(j);
		}
    }
}

7、结束外部循环

break只能结束当前循环

public class HelloWorld {
    public static void main(String[] args) {
         
        //打印单数     
    	for (int i = 0; i < 10; i++) {
    		
            for (int j = 0; j < 10; j++) {
            	System.out.println(i+":"+j);
            	if(0==j%2)  
            		break; //如果是双数,结束当前循环
    		}
			
		}
    	
    }
}

使用boolean变量结束外部循环

借助boolean变量结束外部循环
需要在内部循环中修改这个变量值
每次内部循环结束后,都要在外部循环中判断,这个变量的值

public class HelloWorld {
	public static void main(String[] args) {
		boolean breakout = false; //是否终止外部循环的标记
		for (int i = 0; i < 10; i++) {

			for (int j = 0; j < 10; j++) {
				System.out.println(i + ":" + j);
				if (0 == j % 2) {
					breakout = true; //终止外部循环的标记设置为true
					break;
				}
			}
			if (breakout) //判断是否终止外部循环
				break;
		}

	}
}

使用标签结束外部循环

在外部循环的前一行,加上标签
在break的时候使用该标签
即能达到结束外部循环的效果

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; //如果是双数,结束外部循环
    		}
			
		}
    	
    }
}

1.5 数组

1、创建数组

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

声明数组

int[] a; 声明了一个数组变量。
[]表示该变量是一个数组
int 表示数组里的每一个元素都是一个整数
a 是变量名
但是,仅仅是这一句声明,不会创建数组

有时候也会写成int a[]; 没有任何区别,就是你看哪种顺眼的问题

创建数组

创建数组的时候,要指明数组的长度。
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; 
		//创建一个长度是5的数组,并且使用引用a指向该数组
		a = new int[5];
		
		int[] b = new int[5]; //声明的同时,指向一个数组
		
	}
}

访问数组 

数组下标基0
下标0,代表数组里的第一个数

数组长度

.length属性用于访问一个数组的长度
数组访问下标范围是0到长度-1
一旦超过这个范围,就会产生数组下标越界异常

2、初始化数组

分配空间与赋值分步进行

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

3、排序

选择法排序

选择法排序的思路:
把第一位和其他所有的进行比较,只要比第一位小的,就换到第一个位置来
比较完后,第一位就是最小的
然后再从第二位和剩余的其他所有进行比较,只要比第二位小,就换到第二个位置来
比较完后,第二位就是第二小的
以此类推

public class HelloWorld {
	public static void main(String[] args) {
		int a [] = new int[]{18,62,68,82,65,9};
		//排序前,先把内容打印出来
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println(" ");
		//选择法排序
	
		//第一步: 把第一位和其他所有位进行比较
		//如果发现其他位置的数据比第一位小,就进行交换
		
		for (int i = 1; i < a.length; i++) {
			if(a[i]<a[0]){   
				int temp = a[0];
				a[0] = a[i];
				a[i] = temp;
			}
		}
		//把内容打印出来
		//可以发现,最小的一个数,到了最前面
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println(" ");
		
		//第二步: 把第二位的和剩下的所有位进行比较
		for (int i = 2; i < a.length; i++) {
			if(a[i]<a[1]){   
				int temp = a[1];
				a[1] = a[i];
				a[i] = temp;
			}
		}
		//把内容打印出来
		//可以发现,倒数第二小的数,到了第二个位置
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println(" ");		
		
		//可以发现一个规律
		//移动的位置是从0 逐渐增加的
		//所以可以在外面套一层循环
		
		for (int j = 0; j < a.length-1; j++) {
			for (int i = j+1; i < a.length; i++) {
				if(a[i]<a[j]){   
					int temp = a[j];
					a[j] = a[i];
					a[i] = temp;
				}
			}
		}
		
		//把内容打印出来
		for (int i = 0; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println(" ");		
	}
}

冒泡法排序

问 

冒泡法排序的思路:
第一步:从第一位开始,把相邻两位进行比较
如果发现前面的比后面的大,就把大的数据交换在后面,循环比较完毕后,最后一位就是最大的
第二步: 再来一次,只不过不用比较最后一位
以此类推

public class HelloWorld {
    public static void main(String[] args) {
        int a [] = new int[]{18,62,68,82,65,9};
        //排序前,先把内容打印出来
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println(" ");
        //冒泡法排序
     
        //第一步:从第一位开始,把相邻两位进行比较
        //如果发现前面的比后面的大,就把大的数据交换在后面
         
        for (int i = 0; i < a.length-1; i++) {
            if(a[i]>a[i+1]){   
                int temp = a[i];
                a[i] = a[i+1];
                a[i+1] = temp;
            }
        }
        //把内容打印出来
        //可以发现,最大的到了最后面
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println(" ");
         
        //第二步: 再来一次,只不过不用比较最后一位
        for (int i = 0; i < a.length-2; i++) {
            if(a[i]>a[i+1]){   
                int temp = a[i];
                a[i] = a[i+1];
                a[i+1] = temp;
            }
        }
        //把内容打印出来
        //可以发现,倒数第二大的到了倒数第二个位置
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println(" ");        
         
        //可以发现一个规律
        //后边界在收缩
        //所以可以在外面套一层循环
         
        for (int j = 0; j < a.length; j++) {
            for (int i = 0; i < a.length-j-1; i++) {
                if(a[i]>a[i+1]){   
                    int temp = a[i];
                    a[i] = a[i+1];
                    a[i+1] = temp;
                }
            }
        }
         
        //把内容打印出来
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println(" ");        
    }
}

4、增强型for循环

增强型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);
		}
        
    }
}

5、复制数组

数组的长度是不可变的,一旦分配好空间,是多长,就多长,不能增加也不能减少。

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

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] + " ");
        }

    }
}

6、二维数组

这是一个一维数组, 里面的每一个元素,都是一个基本类型int

int a[] =new int[]{1,2,3,4,5};


这是一个二维数组,里面的每一个元素,都是一个一维数组
所以二维数组又叫数组的数组

int b[][] = new int[][]{

{1,2,3},

{4,5,6},

{7,8,9}

};

初始化二维数组

public class HelloWorld {
	public static void main(String[] args) {
	   //初始化二维数组,
	   int[][] a = new int[2][3]; //有两个一维数组,每个一维数组的长度是3
	   a[1][2] = 5;  //可以直接访问一维数组,因为已经分配了空间
	     
	   //只分配了二维数组
	   int[][] b = new int[2][]; //有两个一维数组,每个一维数组的长度暂未分配
	   b[0]  =new int[3]; //必须事先分配长度,才可以访问
	   b[0][2] = 5;
	   
	   //指定内容的同时,分配空间
	   int[][] c = new int[][]{
			   {1,2,4},
			   {4,5},
			   {6,7,8,9}
	   };

    }
}

7、数组Arrays

Arrays是针对数组的工具类,可以进行 排序,查找,复制填充等功能。 大大提高了开发人员的工作效率。

数组复制(copyOfRange)

使用System.arraycopy进行数组复制类似的, Arrays提供了一个copyOfRange方法进行数组复制。
不同的是System.arraycopy,需要事先准备好目标数组,并分配长度。 copyOfRange 只需要源数组就就可以了,通过返回值,就能够得到目标数组了。
除此之外,需要注意的是 copyOfRange 的第3个参数,表示源数组的结束位置,是取不到的。

import java.util.Arrays;

public class HelloWorld {
	public static void main(String[] args) {
		int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

		// copyOfRange(int[] original, int from, int to)
		// 第一个参数表示源数组
		// 第二个参数表示开始位置(取得到)
		// 第三个参数表示结束位置(取不到)
		int[] b = Arrays.copyOfRange(a, 0, 3);

		for (int i = 0; i < b.length; i++) {
			System.out.print(b[i] + " ");
		}

	}
}

转换为字符串(toString)

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

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

import java.util.Arrays;
 
public class HelloWorld {
    public static void main(String[] args) {
        int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
        String content = Arrays.toString(a);
        System.out.println(content);
 
    }
}

排序(sort)

在前面章节学习了 选择法排序冒泡法排序,Arrays工具类提供了一个sort方法,只需要一行代码即可完成排序功能。

import java.util.Arrays;
 
public class HelloWorld {
    public static void main(String[] args) {
        int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
        System.out.println("排序之前 :");
        System.out.println(Arrays.toString(a));
        Arrays.sort(a);
        System.out.println("排序之后:");
        System.out.println(Arrays.toString(a));
 
    }
}

搜索(binarySearch)

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

import java.util.Arrays;

public class HelloWorld {
	public static void main(String[] args) {
		int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

		Arrays.sort(a);

		System.out.println(Arrays.toString(a));
		//使用binarySearch之前,必须先使用sort进行排序
		System.out.println("数字 62出现的位置:"+Arrays.binarySearch(a, 62));
	}
}

判断是否相同(equals)

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

import java.util.Arrays;

public class HelloWorld {
	public static void main(String[] args) {
		int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
		int b[] = new int[] { 18, 62, 68, 82, 65, 8 };

		System.out.println(Arrays.equals(a, b));
	}
}

填充(fill)

使用同一个值,填充整个数组

import java.util.Arrays;
 
public class HelloWorld {
    public static void main(String[] args) {
        int a[] = new int[10];
 
        Arrays.fill(a, 5);
 
        System.out.println(Arrays.toString(a));
 
    }
}

  1.6 类和对象

1、引用

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

引用和指向

new Hero();


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

Hero h = new Hero();


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

public class Hero {
     
    String name; //姓名
     
    float hp; //血量
     
    float armor; //护甲
     
    int moveSpeed; //移动速度
     
    public static void main(String[] args) {
    	//创建一个对象
        new Hero();
        
        //使用一个引用来指向这个对象
        Hero h = new Hero();
        
    }   
     
}

多个引用,一个对象

引用有多个,但是对象只有一个。
在这个例子里,所有引用都指向了同一个对象。
对象就像 "房产", 引用就像"房产证"
房产证的复印件可以有多张,但是真正的"房产" 只有这么一处

public class Hero {
     
    String name; //姓名
     
    float hp; //血量
     
    float armor; //护甲
     
    int moveSpeed; //移动速度
     
    public static void main(String[] args) {
        
        //使用一个引用来指向这个对象
        Hero h1 = new Hero();
        Hero h2 = h1;  //h2指向h1所指向的对象
        Hero h3 = h1;
        Hero h4 = h1;
        Hero h5 = h4;
        
        //h1,h2,h3,h4,h5 五个引用,都指向同一个对象
        
    }   
     
}

 一个引用,多个对象

第8行,引用garen指向新创建的对象(对象1)
第9行,同一个引用garen指向新创建的对象(对象2)
这个时候,对象1,就没有任何引用指向了
换句话说,就没有任何手段控制和访问该对象,那么该对象就变得没有意义。

package charactor;

public class Hero {
	public String name; 
	protected float hp; 

	public static void main(String[] args) {
	       Hero garen =  new Hero();
	       garen =  new Hero();
	}
}

2、继承

在LOL中,武器是物品的一种,也是有名称和价格的
所以在设计类的时候,可以让武器继承物品,从而继承名称和价格属性

物品类

public class Item {
	String name;
	int price;
}

武器类Weapon(不继承)

武器类: Weapon不继承Item的写法
独立设计 name和price属性
同时多了一个属性 damage 攻击力

public class Weapon{
	String name;
	int price;
	int damage; //攻击力

}

武器类Weapon(继承类Item)

这一次Weapon继承Item
虽然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;
		
	}
	
}

3、方法重载

方法的重载指的是方法名一样,但是参数类型不一样

attack方法的重载

有一种英雄,叫做物理攻击英雄 ADHero
为ADHero 提供三种方法

public void attack()

public void attack(Hero h1)

public void attack(Hero h1, Hero h2)


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

public class ADHero extends Hero {
	public void attack() {
		System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
	}

	public void attack(Hero h1) {
		System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
	}

	public void attack(Hero h1, Hero h2) {
		System.out.println(name + "同时对" + h1.name + "和" + h2.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 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);

	}

}

4、构造方法

通过一个类创建一个对象,这个过程叫做实例化

实例化是通过调用构造方法(又叫做构造器)实现的

什么是构造方法

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

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(){  
	//		System.out.println("调用Hero的构造方法");
	//	}

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

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

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

public class Hero {
     
    String name; //姓名
     
    float hp; //血量
     
    float armor; //护甲
     
    int moveSpeed; //移动速度
     
    //有参的构造方法
    //默认的无参的构造方法就失效了
    public Hero(String heroname){  
    	name = heroname;
    }
     
    public static void main(String[] args) {
        Hero garen =  new Hero("盖伦");  
         
        Hero teemo =  new 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);
    }
    
}

5、this

this即代表当前对象

public class Hero {
	
	String name; //姓名
	
	float hp; //血量
	
	float armor; //护甲
	
	int moveSpeed; //移动速度

	//打印内存中的虚拟地址
	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();
	}	
	
}

通过this访问属性

通过this关键字访问对象的属性

public class Hero {
	
	String name; //姓名
	
	float hp; //血量
	
	float armor; //护甲
	
	int moveSpeed; //移动速度

	//参数名和属性名一样
	//在方法体中,只能访问到参数name
	public void setName1(String name){
		name = name;
	}
	
	//为了避免setName1中的问题,参数名不得不使用其他变量名
	public void setName2(String heroName){
		name = heroName;
	}
	
	//通过this访问属性
	public void setName3(String name){
		//name代表的是参数name
		//this.name代表的是属性name
		this.name = name;
	}
	
	public static void main(String[] args) {
		Hero  h =new Hero();
		
		h.setName1("teemo");
		System.out.println(h.name);
		
		h.setName2("garen");
		System.out.println(h.name);		
		
		h.setName3("死歌");
		System.out.println(h.name);		
	}
	
}

通过this调用其他的构造方法

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

public class Hero {
       
    String name; //姓名
       
    float hp; //血量
       
    float armor; //护甲
       
    int moveSpeed; //移动速度
       
    //带一个参数的构造方法
    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);
        
    }
     
}

6、传参

变量有两种类型 基本类型 和类类型

参数也是变量,所以传参分为
基本类型传参
类类型传参

基本类型传参

基本类型传参
在方法内,无法修改方法外的基本类型参数

public class Hero {
       
    String name; //姓名
       
    float hp; //血量
       
    float armor; //护甲
       
    int moveSpeed; //移动速度
    
    public Hero(){
    	
    }
    
    //回血
    public void huixue(int xp){
    	hp = hp + xp;
    	//回血完毕后,血瓶=0
    	xp=0;
    }
     
    public Hero(String name,float hp){
    	this.name = name;
        this.hp = hp;
    }

    public static void main(String[] args) {
        Hero teemo =  new Hero("提莫",383);
        //血瓶,其值是100
        int xueping = 100;
        
        //提莫通过这个血瓶回血
        
        teemo.huixue(xueping);
        
        System.out.println(xueping);
        
    }
     
}

引用与=

如果一个变量是基本类型
比如 int hp = 50;
我们就直接管hp叫变量
=表示赋值的意思。
如果一个变量是类类型
比如 Hero h = new Hero();
我们就管h叫做引用。
=不再是赋值的意思
=表示指向的意思
比如 Hero h = new Hero();
这句话的意思是
引用h,指向一个Hero对象

类类型传参

类类型又叫引用
第24行的引用 teemo与 第17行的引用hero,是不同的引用
通过调用garen.attack(teemo, 100); 使得这两个引用都指向了同一个对象
所以在第18行hero.hp = hero.hp - damage; 就使得该对象的hp值,发生了变化
因此第25行,打印该对象的Hp值就是变化后的值

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);
		System.out.println(teemo.hp);
	}

}

7、包

把比较接近的类,规划在同一个包下

Hero,ADHero 规划在一个包,叫做charactor(角色)
Item,Weapon规划在另一个包下,叫做 property(道具)
在最开始的地方声明该类所处于的包名

package charactor; //在最开始的地方声明该类所处于的包名
public class Hero {
       
    String name; //姓名
       
    float hp; //血量
       
    float armor; //护甲
       
    int moveSpeed; //移动速度
    
}

使用其他包下的类,必须import

使用同一个包下的其他类,直接使用即可
但是要使用其他包下的类,必须import

package charactor;

//Weapon类在其他包里,使用必须进行import
import property.Weapon;

public class Hero {
       
    String name; //姓名
       
    float hp; //血量
       
    float armor; //护甲
       
    int moveSpeed; //移动速度
    
    //装备一把武器
    public void equip(Weapon w){
    	
    }
       
}

8、访问修饰符

成员变量有四种修饰符
private 私有的
package/friendly/default 不写
protected 受保护的
public 公共的

比如public 表示公共的

public String name;


而maxHP 没有修饰符即代表package/friendly/default

float maxHP

类之间的关系

类和类之间的关系有如下几种:
以Hero为例
自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类

private私有的

使用private修饰属性
自身:是可以访问的
同包子类:不能继承
不同包子类:不能继承
同包类:不能访问
其他包类:不能访问

package charactor;

import property.Weapon;

public class Hero {

	//属性id是private的,只有Hero自己可以访问
	//子类不能继承
	//其他类也不能访问
	private int id;
	
	String name;

	float hp;

	float armor;

	int moveSpeed;

	public void equip(Weapon w) {

	}

}

package/friendly/default 不写

没有修饰符即代表package/friendly/default
float maxHP; 血量上限

package charactor;

import property.Weapon;

public class Hero {
	private int id;

	String name;

	// 无修饰符的属性 hp
	// 自己可以访问

	// 同包子类可以继承
	// 不同包子类不能继承

	// 同包类可以访问
	// 不同包类不能访问
	float hp;

	float armor;

	int moveSpeed;

	public void equip(Weapon w) {

	}

}

protected受保护的

受保护的修饰符
protected float hp; 血量

package charactor;

import property.Weapon;

public class Hero {
	private int id;

	String name;

	// protected饰符的属性 hp
	// 自己可以访问

	// 同包子类可以继承
	// 不同包子类可以继承

	// 同包类可以访问
	// 不同包类不能访问
	protected float hp;

	float armor;

	int moveSpeed;

	public void equip(Weapon w) {

	}

}

 public公共的

公共的修饰符
public String name; 姓名
任何地方,都可以访问

package charactor;

import property.Weapon;

public class Hero {
	private int id;

	// public的属性 name
	// 自己可以访问

	// 同包子类可以继承
	// 不同包子类可以继承

	// 同包类可以访问
	// 不同包类可以访问
	public String name;

	protected float hp;

	float armor;

	int moveSpeed;

	public void equip(Weapon w) {

	}

}

总结

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

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

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


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

9、类属性

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值
与对象属性对比:
不同对象的 对象属性 的值都可能不一样。
比如盖伦的hp 和 提莫的hp 是不一样的。
但是所有对象的类属性的值,都是一样的

类属性

类属性: 又叫做静态属性
对象属性: 又叫实例属性,非静态属性
如果一个属性声明成类属性,那么所有的对象,都共享这么一个值
给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。

package charactor;

public class Hero {
	public String name; //实例属性,对象属性,非静态属性
	protected float hp; 
	static String copyright;//类属性,静态属性
	
	public static void main(String[] args) {
	       Hero garen =  new Hero();
           garen.name = "盖伦";
           
           Hero.copyright = "版权由Riot Games公司所有";
           
           System.out.println(garen.name);
           System.out.println(garen.copyright);
           
           Hero teemo =  new Hero();
           teemo.name = "提莫";
           System.out.println(teemo.name);     
           System.out.println(teemo.copyright);
		
	}
	
}

访问类属性

访问类属性有两种方式
1. 对象.类属性

teemo.copyright
2. 类.类属性

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

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

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

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

10、类方法

类方法: 又叫做静态方法

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

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

类方法

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

调用类方法

访问类属性一样,调用类方法也有两种方式
1. 对象.类方法

garen.battleWin();
2. 类.类方法

Hero.battleWin();
这两种方式都可以调用类方法,但是建议使用第二种 类.类方法 的方式进行,这样更符合语义上的理解。
并且在很多时候,并没有实例,比如在前面练习的时候用到的随机数的获取办法

Math.random()
random()就是一个类方法,直接通过类Math进行调用,并没有一个Math的实例存在。

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

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

public String getName(){

return name;

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

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

public static void printGameDuration(){

System.out.println("已经玩了10分50秒");

}
printGameDuration 打印当前玩了多长时间了,不和某一个具体的英雄关联起来,所有的英雄都是一样的。 这样的方法,更带有功能性色彩
就像取随机数一样,random()是一个功能用途的方法

Math.random()

11、属性初始化

对象属性初始化

对象属性初始化有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; //构造方法中初始化
		
	}
	
}

类属性初始化

类属性初始化有2种
1. 声明该属性的时候初始化
2. 静态初始化块

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

12、单例模式

单例模式

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

饿汉式单例模式

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

package charactor;

public class GiantDragon {

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

	//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个

	private static GiantDragon instance = new GiantDragon();
	
	//public static 方法,提供给调用者获取12行定义的对象
	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方法,返回第二步的静态属性

13、枚举类型

预先定义的变量

枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量
比如设计一个枚举类型 季节,里面有4种常量
public enum Season {

SPRING,SUMMER,AUTUMN,WINTER

}
一个常用的场合就是switch语句中,使用枚举来进行判断

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

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

使用枚举的好处

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

SPRING,SUMMER,AUTUMN,WINTER
而不会出现奇怪的 第5季

public class HelloWorld {
    public static void main(String[] args) {
        int season = 5;
        switch (season) {
        case 1:
            System.out.println("春天");
            break;
        case 2:
            System.out.println("夏天");
            break;
        case 3:
            System.out.println("秋天");
            break;
        case 4:
            System.out.println("冬天");
            break;
        }
    }
}

遍历枚举

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

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

1.7 接口和继承

1、接口

在设计LOL的时候,进攻类英雄有两种,一种是进行物理系攻击,一种是进行魔法系攻击

这时候,就可以使用接口来实现这个效果。

接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。

物理攻击接口

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

1.选择文件夹的包名,右键【New】------>【Java Class】,

2.在弹出的【Create New Class】窗口中,Name选择框中输入接口名称,Kind选择框中选择【Interface】类型。

package charactor;

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

设计一类英雄,能够使用物理攻击

设计一类英雄,能够使用物理攻击,这类英雄在LOL中被叫做AD
类:ADHero
继承了Hero 类,所以继承了name,hp,armor等属性

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

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

package charactor;

public class ADHero extends Hero implements AD{

	@Override
	public void physicAttack() {
		System.out.println("进行物理攻击");
	}

}

魔法攻击接口

创建一个接口AP ,声明一个方法 magicAttack 魔法攻击,但是没有方法体,是一个“空”方法

package charactor;

public interface AP {

	public void magicAttack();
}

设计一类英雄,只使用魔法攻击

设计一类英雄,只能使用魔法攻击,这类英雄在LOL中被叫做AP
类:APHero
继承了Hero 类,所以继承了name,hp,armor等属性
同时,实现了AP这个接口,就必须提供AP接口中声明的方法magicAttack()
实现在语法上使用关键字 implements

package charactor;

public class APHero extends Hero implements AP{

	@Override
	public void magicAttack() {
		System.out.println("进行魔法攻击");
	}

}

设计一类英雄,既能使用物理攻击,又能使用魔法攻击

一种英雄,能够同时进行物理攻击和魔法攻击
比如伊泽瑞尔,皮城女警凯特琳

package charactor;
 
//同时能进行物理和魔法伤害的英雄
public class ADAPHero extends Hero implements AD,AP{
 
    @Override
    public void magicAttack() {
        System.out.println("进行魔法攻击");
    }
 
    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
 
}

什么样的情况下该使用接口?

如上的例子,似乎要接口,不要接口,都一样的,那么接口的意义是什么呢

学习一个知识点,是由浅入深得进行的。 这里呢,只是引入了接口的概念,要真正理解接口的好处,需要更多的实践,以及在较为复杂的系统中进行大量运用之后,才能够真正理解,比如在学习了多态之后就能进一步加深理解。

刚刚接触一个概念,就希望达到炉火纯青的学习效果,这样的学习目标是不科学的。

2、对象转型

明确引用类型与对象类型的概念

首先,明确引用类型与对象类型的概念
在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
对象是有类型的, 是ADHero
引用也是有类型的,是ADHero
通常情况下,引用类型和对象类型是一样的
接下来要讨论的类型转换的问题,指的是引用类型和对象类型不一致的情况下的转换问题

package charactor;

public class Hero {
	public String name; 
	protected float hp;
	
	public static void main(String[] args) {
		
		ADHero ad = new ADHero();
		
	}
}

子类转父类(向上转型)

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

到底能否转换成功? 教大家一个很简单的判别办法
把右边的当做左边来用,看说得通不
 

Hero h = new Hero();

ADHero ad = new ADHero();

h = ad;


右边ad引用所指向的对象的类型是 物理攻击英雄
左边h引用的类型是 普通英雄
把物理攻击英雄 当做 普通英雄,说不说得通? 说得通,就可以转

所有的子类转换为父类,都是说得通的。比如你身边的例子

苹果手机 继承了 手机,把苹果手机当做普通手机使用
怡宝纯净水 继承了 饮品, 把怡宝纯净水 当做饮品来使用
苍老师 继承了动物, 把苍老师 。。。

package charactor;

public class Hero {
	public String name; 
	protected float hp;
	
	public static void main(String[] args) {
		
		Hero h = new Hero();
		
		ADHero ad = new ADHero();
		
		//类型转换指的是把一个引用所指向的对象的类型,转换为另一个引用的类型
		
		//把ad引用所指向的对象的类型是ADHero
		//h引用的类型是Hero
		//把ADHero当做Hero使用,一定可以 
		
		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。 从语义上讲,把物理攻击英雄,当成辅助英雄来用,说不通,所以会强制转换失败,并且抛出异常

以下是对完整的代码的关键行分析
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 类型转换异常

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

没有继承关系的两个类,互相转换

没有继承关系的两个类,互相转换,一定会失败
虽然ADHero和APHero都继承了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();

		// 没有继承关系的类型进行互相转换一定会失败,所以会出现编译错误
		ad = (ADHero) ap;

	}

}

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

引用ad指向的对象是ADHero类型,这个类型实现了AD接口
10行: 把一个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;
         
    }
      
}

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

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();
           
        AD adi = ad;
  
        ADHero adHero = (ADHero) adi;
           
        ADAPHero adapHero = (ADAPHero) adi;
        adapHero.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);
    }
}

3、重写

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

父类Item

父类Item有一个方法,叫做effect

package property;

public class Item {
	String name;
	int price;

	public void buy(){
		System.out.println("购买");
	}
	public void effect() {
		System.out.println("物品使用后,可以有效果");
	}

}

子类LifePotion

子类LifePotion继承Item,同时也提供了方法effect

package property;

public class LifePotion extends Item{
	
	public void effect(){
		System.out.println("血瓶使用后,可以回血");
	}
	
}

调用重写的方法

调用重写的方法
调用就会执行重写的方法,而不是从父类的方法
所以LifePotion的effect会打印:
"血瓶使用后,可以回血"

package property;

public class Item {
	String name;
	int price;
	
	public void effect(){
		System.out.println("物品使用后,可以有效果");
	}
	
	public static void main(String[] args) {
		Item i = new Item();
		i.effect();
		
		LifePotion lp =new LifePotion();
		lp.effect();
	}
	
}

如果没有重写这样的机制怎么样?

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

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

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

package property;

public class Item {
	String name;
	int price;

	public void buy(){
		System.out.println("购买");
	}
	public void effect() {
		System.out.println("物品使用后,可以有效果");
	}

}

4、多态

操作符的多态
+ 可以作为算数运算,也可以作为字符串连接
类的多态
父类引用指向子类对象

操作符的多态

同一个操作符在不同情境下,具备不同的作用
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接

package charactor;
  
public class Hero {
    public String name; 
    protected float hp;

    public static void main(String[] args) {
    	
    	int i = 5;
    	int j = 6;
    	int k = i+j; //如果+号两侧都是整型,那么+代表 数字相加
    	
    	System.out.println(k);
    	
    	int a = 5;
    	String b = "5";
    	
    	String c = a+b; //如果+号两侧,任意一个是字符串,那么+代表字符串连接
    	System.out.println(c);
    	
    }
      
}

观察类的多态现象

观察类的多态现象:
1. i1和i2都是Item类型
2. 都调用effect方法
3. 输出不同的结果

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态

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. 调用的方法有重写
那么多态有什么作用呢? 通过比较不使用多态使用多态来进一步了解

类的多态-不使用多态

如果不使用多态,
假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion
useMagicPotion
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion 净化药水
useGuard 守卫
useInvisiblePotion 使用隐形药水
等等等等

package charactor;

import property.LifePotion;
import property.MagicPotion;
  
public class Hero {
    public String name; 
    protected float hp;

    public void useLifePotion(LifePotion lp){
    	lp.effect();
    }
    public void useMagicPotion(MagicPotion mp){
    	mp.effect();
    }

    public static void main(String[] args) {
    	
    	Hero garen =  new Hero();
        garen.name = "盖伦";
    
    	LifePotion lp =new LifePotion();
    	MagicPotion mp =new MagicPotion();
    	
    	garen.useLifePotion(lp);
    	garen.useMagicPotion(mp);
    	
    }
      
}

类的多态-使用多态

如果物品的种类特别多,那么就需要设计很多的方法
比如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);    	
    	
    }
      
}

5、隐藏

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

父类

父类有一个类方法 :battleWin

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

6、super

准备一个显示提供无参构造方法的父类

准备显式提供无参构造方法的父类
在实例化Hero对象的时候,其构造方法会打印
“Hero的构造方法 "

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 static void main(String[] args) {
		new Hero();
	}
     
}

实例化子类,父类的构造方法一定会被调用

实例化一个ADHero(), 其构造方法会被调用
其父类的构造方法也会被调用
并且是父类构造方法先调用
子类构造方法会默认调用父类的 无参的构造方法

package charactor;
 
public class ADHero extends Hero implements AD{
 
    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
    
    public ADHero(){
    	
    	System.out.println("AD Hero的构造方法");
    }
    
    public static void main(String[] args) {

    	new ADHero();
    	
    }
 
}

 父类显式提供两个构造方法

分别是无参的构造方法和带一个参数的构造方法

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

子类显示调用父类带参构造方法

使用关键字super 显式调用父类带参的构造方法

package charactor;
 
public class ADHero extends Hero implements AD{
 
    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
    
    public ADHero(String name){
    	super(name);
    	System.out.println("AD Hero的构造方法");
    }
    
    public static void main(String[] args) {
    	new ADHero("德莱文");
    }
 
}

调用父类属性

通过super调用父类的moveSpeed属性
ADHero也提供了属性moveSpeed

public int getMoveSpeed(){

return this.moveSpeed;

}

public int getMoveSpeed2(){

return super.moveSpeed;

}

package charactor;
 
public class ADHero extends Hero implements AD{

    int moveSpeed=400; //移动速度

    @Override
    public void physicAttack() {
        System.out.println("进行物理攻击");
    }
    
    public int getMoveSpeed(){
    	return this.moveSpeed;
    }
    
    public int getMoveSpeed2(){
    	return super.moveSpeed;
    }
    
    public static void main(String[] args) {
    	ADHero h= new ADHero();
    	
    	System.out.println(h.getMoveSpeed());
    	System.out.println(h.getMoveSpeed2());
    	
    }
 
}

调用父类方法

ADHero重写了useItem方法,并且在useItem中通过super调用父类的useItem方法

package charactor;

import property.Item;
import property.LifePotion;

public class ADHero extends Hero implements AD {

	int moveSpeed = 400; // 移动速度

	@Override
	public void physicAttack() {
		System.out.println("进行物理攻击");
	}

	public int getMoveSpeed() {
		return this.moveSpeed;
	}

	public int getMoveSpeed2() {
		return super.moveSpeed;
	}

	// 重写useItem,并在其中调用父类的userItem方法
	public void useItem(Item i) {
		System.out.println("adhero use item");
		super.useItem(i);
	}

	public static void main(String[] args) {
		ADHero h = new ADHero();

		LifePotion lp = new LifePotion();

	}

}

7、Object类

Object类是所有类的父类

声明一个类的时候,默认是继承了Object
public class Hero extends Object

package charactor;

import property.Item;

public class Hero extends Object {
       
    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();
	}
     
}

toString()

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

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

    }
}

equals()

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

假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

package charactor;
 
public class Hero {
    public String name; 
    protected float hp;
     
    public boolean equals(Object o){
    	if(o instanceof Hero){
    		Hero h = (Hero) o;
    		return this.hp == h.hp;
    	}
    	return false;
    }
     
    public static void main(String[] args) {
    	Hero h1= new Hero();
    	h1.hp = 300;
    	Hero h2= new Hero();
    	h2.hp = 400;
    	Hero h3= new Hero();
    	h3.hp = 300;
    	
    	System.out.println(h1.equals(h2));
    	System.out.println(h1.equals(h3));
    }
}

==

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

package charactor;
 
public class Hero {
    public String name; 
    protected float hp;
     
    public boolean equals(Object o){
    	if(o instanceof Hero){
    		Hero h = (Hero) o;
    		return this.hp == h.hp;
    	}
    	return false;
    }
     
    public static void main(String[] args) {
    	Hero h1= new Hero();
    	h1.hp = 300;
    	Hero h2= new Hero();
    	h2.hp = 400;
    	Hero h3= new Hero();
    	h3.hp = 300;
    	
    	System.out.println(h1==h2);
    	System.out.println(h1==h3);
    	
    }
}

hashCode()

hashCode方法返回一个对象的哈希值,但是在了解哈希值的意义之前,讲解这个方法没有意义。

hashCode的意义,将放在hashcode 原理章节讲解

线程同步相关方法

Object还提供线程同步相关方法
wait()
notify()
notifyAll()
这部分内容的理解需要建立在对线程安全有足够的理解的基础之上,所以会放在线程交互 的章节讲解

getClass()

getClass()会返回一个对象的类对象,属于高级内容,不适合初学者过早接触,关于类对象的详细内容请参考反射机制

8、final

final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。

final修饰类

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

package charactor;

public final class Hero extends Object {
       
    String name; //姓名
       
    float hp; //血量
       
}

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

 final修饰基本类型变量

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

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

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个

package charactor;

public class Hero extends Object {
	
	public static final int itemTotalNumber = 6;//物品栏的数量
       
    String name; //姓名
       
    float hp; //血量
       
    float armor; //护甲
       
    int moveSpeed; //移动速度
    
    public static void main(String[] args) {

    	final Hero h;
    	h  =new Hero();
    	
    	h.hp = 5;
    	
	}
     
}

9、抽象类

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

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

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

抽象类

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

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类可以在不提供抽象方法的前提下,声明为抽象类
一旦一个类被声明为抽象类,就不能够被直接实例化

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的
即便没有显式的声明

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

默认方法

package charactor;
 
public interface AP {
 
	public static final int resistPhysic = 100;
	
	//resistMagic即便没有显式的声明为 public static final
	//但依然默认为public static final
	int resistMagic = 0; 
	
    public void magicAttack();
}

10、内部类

内部类分为四种:
非静态内部类
静态内部类
匿名类
本地类

非静态内部类

非静态内部类 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 class Hero {
    public String name; 
    protected float hp; 
 
    private static void battleWin(){
        System.out.println("battle win");
    }
    
    //敌方的水晶
    static class EnemyCrystal{
    	int hp=5000;
    	
    	//如果水晶的血量为0,则宣布胜利
    	public void checkIfVictory(){
    		if(hp==0){
    			Hero.battleWin();
    			
    			//静态内部类不能直接访问外部类的对象属性
    			System.out.println(name + " win this game");
    		}
    	}
    }
    
    public static void main(String[] args) {
    	//实例化静态内部类
    	Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
    	crystal.checkIfVictory();
	}
 
}

匿名类

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

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

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

在匿名类中使用外部的局部变量

在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final

为什么要声明为final,其机制比较复杂,请参考第二个Hero代码中的解释

注:在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final

package charactor;
  
public abstract class Hero {

    public abstract void attack();
     
    public static void main(String[] args) {

    	//在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final
    	final int damage = 5;
    	
        Hero h = new Hero(){
            public void attack() {
                System.out.printf("新的进攻手段,造成%d点伤害",damage );
            }
        };

    }
     
}

11、默认方法

什么是默认方法

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

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

package charactor;

public interface Mortal {
	public void die();

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

为什么会有默认方法

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

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

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

1.8 数字与字符串

1、装箱拆箱

封装类

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

package digit;

public class TestNumber {

	public static void main(String[] args) {
		int i = 5;
		
		//把一个基本类型的变量,转换为Integer对象 
		Integer it = new Integer(i);
		//把一个Integer对象,转换为一个基本类型的int
		int i2 = it.intValue();
		
	}
}

Number类

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

package digit;

public class TestNumber {

	public static void main(String[] args) {
		int i = 5;
		
		Integer it = new Integer(i);
		//Integer是Number的子类,所以打印true
		System.out.println(it instanceof Number);
	}
}

 基本类型转封装类

package digit;

public class TestNumber {

	public static void main(String[] args) {
		int i = 5;

		//基本类型转换成封装类型
		Integer it = new Integer(i);
		
	}
}

封装类转基本类型

package digit;

public class TestNumber {

	public static void main(String[] args) {
		int i = 5;

		//基本类型转换成封装类型
		Integer it = new Integer(i);
		
		//封装类型转换成基本类型
		int i2 = it.intValue();
		
	}
}

自动装箱

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

package digit;

public class TestNumber {

	public static void main(String[] args) {
		int i = 5;

		//基本类型转换成封装类型
		Integer it = new Integer(i);
		
		//自动转换就叫装箱
		Integer it2 = i;
		
	}
}

自动拆箱

不需要调用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;
         
    }
}

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: 使用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);
        
    }
}

3、数学方法

java.lang.Math提供了一些常用的数学运算方法,并且都是以静态方法的形式存在

四舍五入,随机数,开方,次方,Π,自然常数

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

4、格式化输出

格式化输出

如果不使用格式化输出,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐
使用格式化输出,就可以简洁明了

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

printf和format

printf和format能够达到一模一样的效果,如何通过eclipse查看java源代码 可以看到,在printf中直接调用了format

package digit;
 
public class TestNumber {
 
    public static void main(String[] args) {

    	String name ="盖伦";
    	int kill = 8;
    	String title="超神";
    	
    	String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
    	//使用printf格式化输出
    	System.out.printf(sentenceFormat,name,kill,title);
    	//使用format格式化输出
    	System.out.format(sentenceFormat,name,kill,title);
    	
    }
}

换行符

换行符就是另起一行 --- '\n' 换行(newline)
回车符就是回到一行的开头 --- '\r' 回车(return)
在eclipse里敲一个回车,实际上是回车换行符
Java是跨平台的编程语言,同样的代码,可以在不同的平台使用,比如Windows,Linux,Mac
然而在不同的操作系统,换行符是不一样的
(1)在DOS和Windows中,每行结尾是 “\r\n”;
(2)Linux系统里,每行结尾只有 “\n”;
(3)Mac系统里,每行结尾是只有 "\r"。
为了使得同一个java程序的换行符在所有的操作系统中都有一样的表现,使用%n,就可以做到平台无关的换行

package digit;
 
public class TestNumber {
 
    public static void main(String[] args) {

    	System.out.printf("这是换行符%n");
    	System.out.printf("这是换行符%n");
    	
    }
}

总长度,左对齐,补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、字符

保存一个字符的时候使用char

package character;

public class TestChar {

	public static void main(String[] args) {
		char c1 = 'a';
		char c2 = '1';//字符1,而非数字1
		char c3 = '中';//汉字字符
		char c4 = 'ab'; //只能放一个字符
		
	}
}

char对应的封装类

char对应的封装类是Character
装箱拆箱概念,参考 

拆箱装箱

package character;

public class TestChar {

	public static void main(String[] args) {
		char c1 = 'a';
		Character c = c1; //自动装箱
		c1 = c;//自动拆箱
		
	}
}

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'); //转换为字符串
		
	}
}

常见转义

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、字符串

创建字符串

字符串即字符的组合,在Java中,字符串是一个类,所以我们见到的字符串都是对象
常见创建字符串手段:
1. 每当有一个字面值出现的时候,虚拟机就会创建一个字符串
2. 调用String的构造方法创建一个字符串对象
3. 通过+加号进行字符串拼接也会创建新的字符串对象

package character;

public class TestString {

	public static void main(String[] args) {
		String garen ="盖伦"; //字面值,虚拟机碰到字面值就会创建一个字符串对象
		
		String teemo = new String("提莫"); //创建了两个字符串对象
		
		char[] cs = new char[]{'崔','斯','特'};
		
		String hero = new String(cs);//  通过字符数组创建一个字符串对象
		
		String hero3 = garen + teemo;//  通过+加号进行字符串拼接
	}
}

final

String 被修饰为final,所以是不能被继承的

package character;

public class TestString {

	public static void main(String[] args) {
		MyString str = new MyString();
		
	}
	
        /*这里会报错,因为String不能被继承*/
	static class MyString extends String{
		
	}
	
}

immutable

immutable 是指不可改变的
比如创建了一个字符串对象
String garen ="盖伦";
不可改变的具体含义是指:
不能增加长度
不能减少长度
不能插入字符
不能删除字符
不能修改字符
一旦创建好这个字符串,里面的内容 永远 不能改变

String 的表现就像是一个常量

package character;
 
public  class TestString {
 
    public static void main(String[] args) {
        String garen ="盖伦"; 
        
    }
}

字符串格式化

如果不使用字符串格式化,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐
使用字符串格式化,就可以简洁明了
更多的格式化规则,参考格式化输出

package character;
  
public class TestString {
  
    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";
        
        String sentence2 = String.format(sentenceFormat, name,kill,title);
        
        System.out.println(sentence2);
        
    }
}

字符串长度

length方法返回当前字符串的长度
可以有长度为0的字符串,即空字符串

package character;
  
public class TestString {
  
    public static void main(String[] args) {
 
        String name ="盖伦";
        
        System.out.println(name.length());
        
        String unknowHero = "";
        
        //可以有长度为0的字符串,即空字符串
        System.out.println(unknowHero.length());
         
    }
}

7、操纵字符串

关键字简介
charAt获取字符
toCharArray获取对应的字符数组
subString截取子字符串
split分隔
trim去掉首尾空格
toLowerCase
toUpperCase
大小写
indexOf
lastIndexOf
contains
定位
replaceAll
replaceFirst
替换

获取字符

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

获取对应的字符数组

toCharArray()
获取对应的字符数组

package character;
   
public class TestString {
   
    public static void main(String[] args) {
  
    	String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";

    	char[] cs = sentence.toCharArray(); //获取对应的字符数组
    	
    	System.out.println(sentence.length() == cs.length);
    	
    }
}

截取子字符串

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

分隔

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

去掉首尾空格

trim
去掉首尾空格

package character;
   
public class TestString {
   
    public static void main(String[] args) {
  
    	String sentence = "        盖伦,在进行了连续8次击杀后,获得了 超神 的称号      ";
    	
    	System.out.println(sentence);
    	//去掉首尾空格
    	System.out.println(sentence.trim());
    }
}

大小写

toLowerCase 全部变成小写
toUpperCase 全部变成大写

package character;
   
public class TestString {
   
    public static void main(String[] args) {
  
    	String sentence = "Garen";
    	
    	//全部变成小写
    	System.out.println(sentence.toLowerCase());
    	//全部变成大写
    	System.out.println(sentence.toUpperCase());
    	
    }
}

定位

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("击杀")); //是否包含字符串"击杀"
         
    }
}

替换

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

8、比较字符串

是否是同一个对象

str1和str2的内容一定是一样的!
但是,并不是同一个字符串对象

package character;

public class TestString {

	public static void main(String[] args) {

		String str1 = "the light";
		
		String str2 = new String(str1);
		
		//==用于判断是否是同一个字符串对象
		System.out.println( str1  ==  str2);
		
	}

}

是否是同一个对象-特例

str1 = "the light";

str3 = "the light";


一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
所以在第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);
	}

}

内容是否相同

使用equals进行字符串内容的比较,必须大小写一致
equalsIgnoreCase,忽略大小写判断内容是否一致

package character;
 
public class TestString {
 
    public static void main(String[] args) {
 
        String str1 = "the light";
         
        String str2 = new String(str1);
        
        String str3 = str1.toUpperCase();

        //==用于判断是否是同一个字符串对象
        System.out.println( str1  ==  str2);
        
        System.out.println(str1.equals(str2));//完全一样返回true
        
        System.out.println(str1.equals(str3));//大小写不一样,返回false
        System.out.println(str1.equalsIgnoreCase(str3));//忽略大小写的比较,返回true
        
    }
 
}

是否以子字符串开始或结束

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));//以...结束
         
    }
 
}

9、StringBuffer

StringBuffer是可变长的字符串

追加删除插入反转

append追加
delete 删除
insert 插入
reverse 反转

package character;
 
public class TestString {
 
    public static void main(String[] args) {
        String str1 = "let there ";

        StringBuffer sb = new StringBuffer(str1); //根据str1创建一个StringBuffer对象
        sb.append("be light"); //在最后追加
        
        System.out.println(sb);
        
        sb.delete(4, 10);//删除4-10之间的字符
        
        System.out.println(sb);
        
        sb.insert(4, "there ");//在4这个位置插入 there
        
        System.out.println(sb);
        
        sb.reverse(); //反转
        
        System.out.println(sb);

    }
 
}

长度 容量

为什么StringBuffer可以变长?
和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度
比如说new StringBuffer("the"),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了 参考MyStringBuffer
length: “the”的长度 3
capacity: 分配的总空间 19

注: 19这个数量,不同的JDK数量是不一样的

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());//总空间
 
    }
 
}

10、MyStringBuffer

IStringBuffer接口

package character;
 
public interface IStringBuffer {
    public void append(String str); //追加字符串 
    public void append(char c);  //追加字符
    public void insert(int pos,char b); //指定位置插入字符
    public void insert(int pos,String b); //指定位置插入字符串
    public void delete(int start); //从开始位置删除剩下的
    public void delete(int start,int end); //从开始位置删除结束位置-1
    public void reverse(); //反转
    public int length(); //返回长度
}

value和capacity

value:用于存放字符数组
capacity: 容量
无参构造方法: 根据容量初始化value

public MyStringBuffer(){

value = new char[capacity];

}

package character;

public class MyStringBuffer implements IStringBuffer{

	int capacity = 16;
	int length = 0;
	char[] value;
	public MyStringBuffer(){
		value = new char[capacity];
	}
	
	@Override
	public void append(String str) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void append(char c) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void insert(int pos, char b) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void delete(int start) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void delete(int start, int end) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void reverse() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int length() {
		// TODO Auto-generated method stub
		return 0;
	}

}

带参构造方法

package character;

public class MyStringBuffer implements IStringBuffer{

	int capacity = 16;
	int length = 0;
	char[] value;
	public MyStringBuffer(){
		value = new char[capacity];
	}
	
	//有参构造方法
	public MyStringBuffer(String str){
		if(null!=str)
			value =str.toCharArray();
		
		length = value.length;
		
		if(capacity<value.length)
			capacity  = value.length*2;
	}
	
	@Override
	public void append(String str) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void append(char c) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void insert(int pos, char b) {

	}

	@Override
	public void delete(int start) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void delete(int start, int end) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void reverse() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int length() {
		// TODO Auto-generated method stub
		return length;
	}

	@Override
	public void insert(int pos, String b) {

	}

}

反转reverse

package character;

public class MyStringBuffer implements IStringBuffer {

	int capacity = 16;
	int length = 0;
	char[] value;

	public MyStringBuffer() {
		value = new char[capacity];
	}

	// 有参构造方法
	public MyStringBuffer(String str) {
		this();
		if (null == str)
			return;

		if (capacity < str.length()) {
			capacity = value.length * 2;
			value = new char[capacity];
		}

		if (capacity >= str.length())
			System.arraycopy(str.toCharArray(), 0, value, 0, str.length());

		length = str.length();

	}

	@Override
	public void reverse() {
		for (int i = 0; i < length / 2; i++) {
			char temp = value[i];
			value[i] = value[length - i - 1];
			value[length - i - 1] = temp;
		}
	}

	@Override
	public void append(String str) {
		// TODO Auto-generated method stub

	}

	@Override
	public void append(char c) {
		// TODO Auto-generated method stub

	}

	@Override
	public void insert(int pos, char b) {
		// TODO Auto-generated method stub

	}

	@Override
	public void insert(int pos, String b) {
		// TODO Auto-generated method stub

	}

	@Override
	public void delete(int start) {
		// TODO Auto-generated method stub

	}

	@Override
	public void delete(int start, int end) {
		// TODO Auto-generated method stub

	}

	@Override
	public int length() {
		// TODO Auto-generated method stub
		return length;
	}

	public String toString() {
		char[] realValue = new char[length];

		System.arraycopy(value, 0, realValue, 0, length);

		return new String(realValue);
	}

	public static void main(String[] args) {
		MyStringBuffer sb = new MyStringBuffer("there light");

		sb.reverse();
		System.out.println(sb);

	}

}

插入insert和append

边界条件判断
插入之前,首先要判断的是一些边界条件。 比如插入位置是否合法,插入的字符串是否为空

扩容
1. 要判断是否需要扩容。 如果插入的字符串加上已经存在的内容的总长度超过了容量,那么就需要扩容。
2. 数组的长度是固定的,不能改变的,数组本身不支持扩容。 我们使用变通的方式来解决这个问题。
3. 根据需要插入的字符串的长度和已经存在的内容的长度,计算出一个新的容量。 然后根据这个容量,创建一个新的数组,接着把原来的数组的内容,复制到这个新的数组中来。并且让value这个引用,指向新的数组,从而达到扩容的效果。

插入字符串
1. 找到要插入字符串的位置,从这个位置开始,把原数据看成两段,把后半段向后挪动一个距离,这个距离刚好是插入字符串的长度
2. 然后把要插入的数据,插入这个挪出来的,刚刚好的位置里。

修改length的值
最后修改length的值,是原来的值加上插入字符串的长度

insert(int, char)
参数是字符的insert方法,通过调用insert(int, String) 也就实现了。

append
追加,就是在最后位置插入。 所以不需要单独开发方法,直接调用insert方法,就能达到最后位置插入的效果

package character;
 
public class MyStringBuffer implements IStringBuffer{
 
    int capacity = 16;
    int length = 0;
    char[] value;
    public MyStringBuffer(){
        value = new char[capacity];
    }
     
    //有参构造方法
    public MyStringBuffer(String str){
        this();
        if(null==str)
            return;
         
        if(capacity<str.length()){
            capacity  = value.length*2;
            value=new char[capacity];
        }
         
        if(capacity>=str.length())
            System.arraycopy(str.toCharArray(), 0, value, 0, str.length());
         
        length = str.length();
         
    }
     
    @Override
    public void append(String str) {
        insert(length,str);
    }
 
    @Override
    public void append(char c) {
        append(String.valueOf(c));
         
    }
 
    @Override
    public void insert(int pos, char b) {
        insert(pos,String.valueOf(b));
    }
 
    @Override
    public void delete(int start) {
        // TODO Auto-generated method stub
         
    }
 
    @Override
    public void delete(int start, int end) {
        // TODO Auto-generated method stub
         
    }
 
    @Override
    public void reverse() {
        for (int i = 0; i < length/2; i++) {
            char temp = value[i];
            value[i] = value[length-i-1];
            value[length-i-1] = temp;
        }
    }
 
    @Override
    public int length() {
        // TODO Auto-generated method stub
        return length;
    }
 
    @Override
    public void insert(int pos, String b) {
 
        //边界条件判断
        if(pos<0)
            return;
         
        if(pos>length)
            return;
         
        if(null==b)
            return;
         
        //扩容
        while(length+b.length()>capacity){
            capacity = (int) ((length+b.length())*1.5f);
            char[] newValue = new char[capacity];
            System.arraycopy(value, 0, newValue, 0, length);
            value = newValue;
        }
         
        char[] cs = b.toCharArray();
         
        //先把已经存在的数据往后移
         
        System.arraycopy(value, pos, value,pos+ cs.length, length-pos);
        //把要插入的数据插入到指定位置
        System.arraycopy(cs, 0, value, pos, cs.length);
         
        length = length+cs.length;
         
    }
     
    public String toString(){
         
        char[] realValue = new char[length];
 
        System.arraycopy(value, 0, realValue, 0, length);
         
        return new String(realValue);
         
    }
     
    public static void main(String[] args) {
        MyStringBuffer sb = new MyStringBuffer("there light");
        System.out.println(sb);
        sb.insert(0, "let ");
        System.out.println(sb);
 
        sb.insert(10, "be ");
        System.out.println(sb);
        sb.insert(0, "God Say:");
        System.out.println(sb);
        sb.append("!");
        System.out.println(sb);
        sb.append('?');
        System.out.println(sb);
        sb.reverse();
        System.out.println(sb);
         
    }
 
}

删除delete

package character;

public class MyStringBuffer implements IStringBuffer{

	int capacity = 16;
	int length = 0;
	char[] value;
	public MyStringBuffer(){
		value = new char[capacity];
	}
	
	//有参构造方法
	public MyStringBuffer(String str){
		this();
		if(null==str)
			return;
		
		if(capacity<str.length()){
			capacity  = value.length*2;
			value=new char[capacity];
		}
		
		if(capacity>=str.length())
			System.arraycopy(str.toCharArray(), 0, value, 0, str.length());
		
		length = str.length();
		
	}
	
	@Override
	public void append(String str) {

		insert(length,str);
	}

	@Override
	public void append(char c) {
		append(String.valueOf(c));
		
	}

	@Override
	public void insert(int pos, char b) {
		insert(pos,String.valueOf(b));
	}

	@Override
	public void delete(int start) {
		
		delete(start,length);
	}

	@Override
	public void delete(int start, int end) {
		//边界条件判断
		if(start<0)
			return;
		
		if(start>length)
			return;
		
		if(end<0)
			return;
		
		if(end>length)
			return;
		
		if(start>=end)
			return;
		
		System.arraycopy(value, end, value, start, length- end);
		length-=end-start;
		
	}

	@Override
	public void reverse() {

		for (int i = 0; i < length/2; i++) {
			
			char temp = value[i];
			value[i] = value[length-i-1];
			value[length-i-1] = temp;
		}
		
	}

	@Override
	public int length() {
		// TODO Auto-generated method stub
		return length;
	}

	@Override
	public void insert(int pos, String b) {

		//边界条件判断
		if(pos<0)
			return;
		
		if(pos>length)
			return;
		
		if(null==b)
			return;
		
		//扩容
		while(length+b.length()>capacity){
			capacity = (int) ((length+b.length())*1.5f);
			char[] newValue = new char[capacity];
			System.arraycopy(value, 0, newValue, 0, length);
			value = newValue;
		}
		
		char[] cs = b.toCharArray();
		
		//先把已经存在的数据往后移
		
		System.arraycopy(value, pos, value,pos+ cs.length, length-pos);
		//把要插入的数据插入到指定位置
		System.arraycopy(cs, 0, value, pos, cs.length);
		
		length = length+cs.length;
		
	}
	
	public String toString(){
		
		char[] realValue = new char[length];

		System.arraycopy(value, 0, realValue, 0, length);
		
		return new String(realValue);
		
	}
	
	public static void main(String[] args) {
		MyStringBuffer sb = new MyStringBuffer("there light");
		System.out.println(sb);
		sb.insert(0, "let ");
		System.out.println(sb);

		sb.insert(10, "be ");
		System.out.println(sb);
		sb.insert(0, "God Say:");
		System.out.println(sb);
		sb.append("!");
		System.out.println(sb);
		sb.append('?');
		System.out.println(sb);
		sb.reverse();
		System.out.println(sb);
		
		sb.reverse();
		System.out.println(sb);
		
		sb.delete(0,4);
		System.out.println(sb);
		sb.delete(4);
		System.out.println(sb);

	}

}

 1.9 日期

1、Date

Date类
注意:是java.util.Date;
而非 java.sql.Date,此类是给数据库访问的时候使用的

时间原点概念

所有的数据类型,无论是整数,布尔,浮点数还是字符串,最后都需要以数字的形式表现出来。

日期类型也不例外,换句话说,一个日期,比如2020年10月1日,在计算机里,会用一个数字来代替。

那么最特殊的一个数字,就是零. 零这个数字,就代表Java中的时间原点,其对应的日期是1970年1月1日 8点0分0秒 。 (为什么是8点,因为中国的太平洋时区是UTC-8,刚好和格林威治时间差8个小时)

为什么对应1970年呢? 因为1969年发布了第一个 UNIX 版本:AT&T,综合考虑,当时就把1970年当做了时间原点。

所有的日期,都是以为这个0点为基准,每过一毫秒,就+1。

创建日期对象

package date;

//
import java.util.Date;

public class TestDate {

	public static void main(String[] args) {

		// 当前时间
		Date d1 = new Date();
		System.out.println("当前时间:");
		System.out.println(d1);
		System.out.println();
		// 从1970年1月1日 早上8点0分0秒 开始经历的毫秒数
		Date d2 = new Date(5000);
		System.out.println("从1970年1月1日 早上8点0分0秒 开始经历了5秒的时间");
		System.out.println(d2);

	}
}

 getTime

getTime() 得到一个long型的整数
这个整数代表 从1970.1.1 08:00:00:000 开始 每经历一毫秒,增加1
直接打印对象,会看到 “Tue Jan 05 09:51:48 CST 2016” 这样的格式,可读性比较差,为了获得“2016/1/5 09:51:48”这样的格式 请参考日期格式化

package date;
 
//
import java.util.Date;
 
public class TestDate {
 
    public static void main(String[] args) {
        //注意:是java.util.Date;
        //而非 java.sql.Date,此类是给数据库访问的时候使用的
        Date now= new Date();
        //打印当前时间
        System.out.println("当前时间:"+now.toString());
        //getTime() 得到一个long型的整数
        //这个整数代表 1970.1.1 08:00:00:000,每经历一毫秒,增加1
        System.out.println("当前时间getTime()返回的值是:"+now.getTime());
         
        Date zero = new Date(0);
        System.out.println("用0作为构造方法,得到的日期是:"+zero);
         
    }
}

System.currentTimeMills()

当前日期的毫秒数
new Date().getTime() 和 System.currentTimeMillis() 是一样的
不过由于机器性能的原因,可能会相差几十毫秒,毕竟每执行一行代码,都是需要时间的

package date;
  
//
import java.util.Date;
  
public class TestDate {
  
    public static void main(String[] args) {
        Date now= new Date();
 
        //当前日期的毫秒数
        System.out.println("Date.getTime() \t\t\t返回值: "+now.getTime());
        //通过System.currentTimeMillis()获取当前日期的毫秒数
        System.out.println("System.currentTimeMillis() \t返回值: "+System.currentTimeMillis());
          
    }
}

 2、日期格式化

SimpleDateFormat 日期格式化类

日期转字符串

y 代表年

M 代表月

d 代表日

H 代表24进制的小时

h 代表12进制的小时

m 代表分钟

s 代表秒

S 代表毫秒

package date;
 
//
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class TestDate {
 
    public static void main(String[] args) {
         
        //y 代表年
        //M 代表月
        //d 代表日
        //H 代表24进制的小时
        //h 代表12进制的小时
        //m 代表分钟
        //s 代表秒
        //S 代表毫秒
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
        Date d= new Date();
        String str = sdf.format(d);
        System.out.println("当前时间通过 yyyy-MM-dd HH:mm:ss SSS 格式化后的输出: "+str);
        
        SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy-MM-dd" );
        Date d1= new Date();
        String str1 = sdf1.format(d1);
        System.out.println("当前时间通过 yyyy-MM-dd 格式化后的输出: "+str1);
        
    }
}

字符串转日期

模式(yyyy/MM/dd HH:mm:ss)需要和字符串格式保持一致,如果不一样就会抛出解析异常ParseException

关于异常的详细讲解在Java 异常 Exception 章节展开

package date;
 
//
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class TestDate {
 
    public static void main(String[] args) {
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" );
 
        String str = "2016/1/5 12:12:12";
         
        try {
            Date d = sdf.parse(str);
            System.out.printf("字符串 %s 通过格式  yyyy/MM/dd HH:mm:ss %n转换为日期对象: %s",str,d.toString());
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
}

3、Calendar

Calendar类即日历类,常用于进行“翻日历”,比如下个月的今天是多久

Calendar与Date进行转换

采用单例模式获取日历对象Calendar.getInstance();

package date;
 
//
import java.util.Calendar;
import java.util.Date;
 
public class TestDate {
 
    public static void main(String[] args) {
        //采用单例模式获取日历对象Calendar.getInstance();
        Calendar c = Calendar.getInstance();
         
        //通过日历对象得到日期对象
        Date d = c.getTime();
 
        Date d2 = new Date(0);
        c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00
    }
}

翻日历

add方法,在原日期上增加年/月/日
set方法,直接设置年/月/日

package date;

import java.text.SimpleDateFormat;
//
import java.util.Calendar;
import java.util.Date;

public class TestDate {

	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	public static void main(String[] args) {
		Calendar c = Calendar.getInstance();
		Date now = c.getTime();
		// 当前日期
		System.out.println("当前日期:\t" + format(c.getTime()));

		// 下个月的今天
		c.setTime(now);
		c.add(Calendar.MONTH, 1);
		System.out.println("下个月的今天:\t" +format(c.getTime()));

		// 去年的今天
		c.setTime(now);
		c.add(Calendar.YEAR, -1);
		System.out.println("去年的今天:\t" +format(c.getTime()));

		// 上个月的第三天
		c.setTime(now);
		c.add(Calendar.MONTH, -1);
		c.set(Calendar.DATE, 3);
		System.out.println("上个月的第三天:\t" +format(c.getTime()));

	}

	private static String format(Date time) {
		return sdf.format(time);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值