部分代码参考了 https://how2j.cn/ 的Java基础部分内容
(一)Java基础学习过程
1.Java的基本数据类型、常量和多态
变量
1.1基本数据类型
整型 浮点型 字符型 布尔型 类 接口 数组
整型:分为byte short int long四种类型 分别占用1 2 4 8个字节 数据范围为-2 ^ (size * 8 - 1)—2 ^ (size * 8 - 1) - 1
在无long类型参与的整型运算中,运算结果皆为int,有long类型参与的整形运算中,运算结果皆为long
Java的整型数据默认为int,若要为long则在数据后加l或L(即使申明为long也要加)
用八进制和十六进制时分别以07和0x开头
long a = 10000000000L;//才是正确的不加(L)就是错误的
int a = 1000L;//也是错误的,因为int和long不兼容
//用八进制和十六进制时分别以07和0x开头
int a = 0703;
int a = 0xaa;
浮点型:分为float和double两种类型 分别占用4和8个字节
Java的浮点型数据默认为double,若要为float则在数据后加f或F(即使申明为float也要加)
float精确到小数点后8位,double为16位,其中第8位和第16都已经出现不精确的现象了
因此浮点数不适合精确度很高的运算
float a = 3.12F;//才是正确的不加(F)就是错误的
double a = 3.14F;//也是错误的,因为float与double不兼容
//float精确到小数点后7位,double为16位,其中第7位和第16都已经出现不精确的现象了
//且以下现象不报错
float a = 3.14159265F;
double b = 1.123456789123456789;
//系统会自动取7和16位
字符类型:写作char 占用2个字节 可采用十六进制编码形式表示。例如:char c1 = ‘\u0061’;
可用ASC码来表示
char a = '中';
char a = 65;
char c1 = '\u0061';
布尔类型:分为true与false 无规定的占用内存量
其中的true与false不可被0与非0替代
1.2基本数据类型的转换
在计算前,若式子中有不同的数据类型,容量小的数据类型会在计算前先自动转化为容量大的数据类型
其中byte short char之间不会相互转换 会先变为int
小类型可自动转化为大类型 大类型需要强制转化为小类型
//例如以下代码
byte a = 1;
byte b = 2;
byte c = (byte) (a + b);//byte c = a + b;是错误的,因为a+b是int类型,不得与byte直接互通
1.3变量与常量
1.3.1变量
变量分为局部变量和成员变量
其作用域是观察离其最近的左大括号的范围
1.3.2常量
基本性质同C语言
1.4转义字符
\n//换行
\\//\
\t//缩进
\'//单引号
\"//双引号
1.5算术运算符
在运算过程中,都需要遵循上面讲的类型变化规则
字符串与任意类型相加,结果均为字符串
以下给出示例
byte a = 10;
short b = 20;
int c = a * b;
//
int a = 10;
long b = 50000L;
double d = 3.14;
long c = a * b;
double e = c / d;
//
String str = "abcdefg";
int a = 111;
System.out.print(str+a);//输出abcdefg111
System.out.print("5+5="+5+5);//输出
1.6赋值运算符
short a = 10;
a += 5;//会自动进行类型转换== a = (short) (a + 5);
a = a + 5;//不会进行自动类型转换
1.7比较运算符
与C语言一致
其中字符串的比较用equals
对于==来说,基本数据类型直接比较大小,非基本数据类型参照以下的话语
==是判断两个人是不是住在同一个地址,而equals是判断同一个地址里住的人是不是同一个
a = 'abc';
b = 'abc';
if (a.equals(b))
{
System.out.print("a与b相等");
}
int n=3;
int m=3;
System.out.println(n==m);//true
String str = new String("hello");
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1==str2);//false
System.out.print(str1.equals(str2));//true
str1 = str;
str2 = str;
System.out.println(str1==str2);//false
1.8逻辑;运算符和三目运算符
与C语言一致
唯一区别是&& || 具有短路效应而& | 没有短路效应
1.9Scanner类的使用
1.实体化Scanner类:
Scanner sc = new Scanner(System.in);
//若要关闭使用:
sc.close();
2.导包:鼠标移至Scanner上导入Java.util.Scanner
3.进行提示性文件输出
System.out.print("请输入你喜欢的内容\n");
4.获取内容
String name = sc.next();
int a = sc.nextInt();
double b = sc.nextDouble();
5.进行输出内容
System.out.println("请输入你喜欢的字符串为"+name+"\n"+"你喜欢的整型与浮点型数字为"+a+"和"+b);
注意,不要把Scanner作为工具类名,不然会报错。可写成ScannerDemo这种
1.9Java的引用数据类型
在Java中,类和方法也和基本数据类型一样,可作为一种属性被引用执行。
2.数组的使用
2.1数组的定义与访问
是多个相同类型的数据的共同存储单位
2.1.1数组的静态初始化
长度确定、类型统一且不可改变
不得先声明后赋值
int [] arr = new int[]{1, 2, 3, 4, 5};//arr是个int数组类型
String [] arr1 = {"zhangsan","lisi","wangwu"};
上下两种方法都可以
2.1.2数组的动态初始化
往往无法确定元素个数与值
只定义了大致长度与元素类型(一定要给出)
int [] arr = new int[5];//初始值为0,0,0,0,0 且中括号后不得加大括号了(与静态数组区别开)
String [] arr1 = {};
其余的正确定义形式
int[]arr = {};
int arr[] = {};
int arr [] = {};
int [] arr [] = {{1}, {2}, {3}};//即二维数组
2.1.3罗列数组中的所有元素
int [] arr = new int [3];
System.out.print(arr);//输出的是数组在内存中地址所映射出来的hashcode
System.out.print(Arrays.toString(arr));//通过Arrays.toString()转成字符串并展示[0,0,0]
//若为
double [] arr = new double[3];
//则输出[0.0,0.0,0.0]
//若为
boolean [] arr = new boolean[3];
//则输出[false,false,false]
//若为
char [] arr = new char[3];
//则输出[ , , ] 实际上为\u0000
//若为
String [] arr = new String[3];
//则输出[null,null,null]
2.1.4数组的访问
数组的长度:数组名.length 在这里,length后面不用加(),因为length是数组的属性,非方法,只有方法才需要加括号
索引:每个存储到数组中的元素都有一个编号 arr[x]
赋值:arr[x] = data;
2.1.4.1数组访问中的异常
两种情况下数组访问会有异常:
1.数组作为全局变量,却只声明没有赋值 (因为所有值自动初始化为null)
2.数组下标越界
2.2数组的遍历
2.2.1for循环遍历
与C语言大体一致
2.2.1.1foreach循环遍历
语法如下:
int [] arr = {1,2,3,4,5};
for (int i:arr)
{
System.out.println((i));
}
//这里的i是指代数组中的一个元素,而非下标
必须从头到尾遍历,整体的速度比for循环更快,但灵活性更差
数组中取到int类型的最大最小值可用以下语句
int a = Integer.MIN_VALUE;
int b = Integer.MAX_VALUE;
2.3数组的内存结构分析
2.3.1JVM的运行过程
在Java编译环境下(JDK),我们对.java文件进行编译,将其变成.class文件。编译完成后,会将.class文件加载至文件系统或网络中,根据限定的规范存储至内存区最后通过执行引擎进行执行。执行完后会有相应的垃圾回收机制进行回收。
2.3.2JVM的内存划分
当.java文件被编译为.class文件后,就会进入到JVM中进行进一步执行。
其中有一个java运行时数据区:
1.程序计数器:与CPU有关,与开发无关
2.本地方法栈:JVM与操作系统功能之间的联系,与开发无关
3.方法区:储存.class文件、常量、静态变量和类信息
4.堆内存:存储对象或数组,用new来创建的都存储于堆内存(即引用数据类型)
5.Java栈:又称虚拟机栈,是方法运行时使用的内存。其中包含了可以存储变量的局部变量表结构
其中,栈中存储基本数据类型,堆中存储对象,数组与引用数据类型
2.4数组的高级使用
2.4.1Java中的二维数组
2.4.1.1二维数组的静态初始化
int [][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
String [][] arr1 = {{"aaa","bbb"},{"ccc","ddd"},{"eee"}};
需要注意的是:
1.二维数组中,每个元素的位置又是一个单独的数组,如上面第二种写法里,第三个元素构成的数组中仅一个元素,而前面都有两个
2.上面的示例中,第一种为规则的二维数组,而第二种为不规则的二维数组
3.二维数组的初始化是用两层大括号
2.4.1.2二维数组的动态初始化
int [][] arr = new int[3][];//此为不规则定义,且不可写大括号,可无限定义列,但会报错(相当于未声明就使用)
int [][] arr1 = new int[3][2];//此为规则定义
int [][] arr2 = {};
int [][] arr3 = new int [][]{};
需要注意的是:动态初始化的数组,在没有扩容前是无法正常使用的。即如第一种写法,我必须在确定其行列具体大小后才可以进行使用
二维数组中,每一行都是一个一维数组,则数组头是一个地址,指向一个一维数组。这与C语言不一致,即C语言是连续分布的,但Java中二维数组的每一个一维数组,其地址是不连续的。
2.4.1.3二维数组的遍历
int x = 5;
int y = 5;
int [][] arr = new int [x][y];
int i, j, h;
for (i = 0;i < x;i++)//先赋初值
{
for (j = 0;j < y;j++)
arr[i][j] = x*10 + y;
}
for (i = 0;i < arr.length;i++)//进行遍历
{
for (j = 0;j < arr[i].length;j++)//注意这边的遍历条件为arr[i].length因为长度可能不规则
{
System.out.print(arr[i][j]);
}
System.out.print("\n");
}
//用foreach进行遍历
for (int[] i : arr)
{
for (int j : i)
System.out.print(j);
System.out.println();
}
2.4.1.4Java产生随机数
//以产生随机数组为例,且最大值为1000
int [][] arr = new int[8][8];
Random ran = new Random();
int i, j;
for (i = 0;i < 8;i++)
{
for (j = 0;j < 8;j++)
{
arr[i][j] = ran.nextInt(1001);
}
}
//第一种输出方法
for (i = 0;i < arr.length;i++)
{
for (j = 0;j < arr[i].length;j++)
{
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
//第二种输出方法
for (i = 0;i < arr.length;i++)
{
System.out.println(Arrays.toString(arr[i]));
}
2.4.2排序算法
同数据结构
2.4.3学会阅读Java API文档
官方JDK1.8在线文档:http://docs.oracle.com/javase/8/docs/api
查看方法:
1.打开本地API文档或在线文档
2.找到索引
3.输入你要查看的类名
4.查看包(java.lang下的类是不需要导包的)
5.查看类的解释和说明
6.查看构造方法有哪几种方式
7.查看有哪些方法
8.找到具体方法后,注意看方法的参数
其中若方法的参数中有static 说明无需接收返回值
2.4.4数组复制与扩容的方法
2.4.4.1数组的复制
1.通过Arrays类
Arrays.copyOf(Object [] original,int newLength);
Arrays.copyOfRange(Object [] original,int from, int to);//复制其中的一段
//但都是从新数组的0处开始复制
Arrays类最后会返回一个数组,所以要我们定义一个数组去接收
2.通过System类
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
//是从新数组下表为destPos的地方开始复制length个长度
System类需要你先定义一个数组,因为参数中需要新数组
2.4.4.2数组的浅层复制和深层复制
若复制的类型是基本数据类型的一维数组,那么复制后是产生一个新的数组,即改变原数组的内容是不会改变新数组的内容的。
而对于二维数组来说则不一样。
我们都知道,二维数组的第一维度存储的是一个地址
那么在浅层复制中,只是在新数组中复制了一遍第一维度数组中所存储的地址,因此,改变原有数组二维中的值,也会改变新数组中的值。JavaAPI中的所有复制都是浅层复制
而在深层复制中,相当于将二维数组的第二维度也进行了复制,其第一维度中的地址是指向新的数组的,因此改变原数组的值,不会影响到新数组。
2.4.4.3数组的扩容
扩容一般在数组的复制中进行
int [] arr = Arrays.copyOf(arr1, arr1.length*2);
(二)Java面向对象学习过程
1.初识面向对象
1.1类和对象
class就是Java中的类
对象就是下式中的 new Hero();
1.1.1指向和引用
//对于以下示例
int a = 5;//其中,a称为变量,=称为赋值,其类型是一个基本类型(int)
Hero h = new Hero();//其中,h仍然是一个变量,但其类型是一个类,不再是基本类型了,我们将h称为引用,右边的 new Hero();是一个对象,=称作指向,即左边的引用指向了右边的对象
//若单单创建一个对象即
new Hero();
//在没有引用指向他的时候,他是没有操作空间的
//即在
Hero h = new Hero();
//我们可以进行操作
h.name = "Bruce";
//因此,应用就是一个工具让我们去控制对象,引用本身不是对象,但他指向了一个对象
//而一个对象多个引用
Hero h1 = new Hero();
Hero h2 = h1;
//那么h1和h2都指向了同一个对象,在JVM中它们指向同一个东西,并非复制了一份
//一个引用多个对象
Hero h1 = new Hero();//对象1
h1 = new Hero();//对象2
//最后h1指向的是第二个对象,即每个引用只能指向一个对象
//由于开发者没有办法操作一个没有引用的对象,因此,对象1满足了垃圾回收的条件,因此被回收掉了
1.1.2类的属性
状态就是一个类的属性
属性可以是基本类型,也可以是类类型
1.1.3类的继承
首先要明确类的继承的意义:当类2继承了类1后,类1的属性都会被类2继承
具体写法如下:
public class Item{
String name;
int price;
}
//再建立一个类
public class Weapon extends Item{
int damage;//这是Weapon类特有的
public static void main(String [] args){
Weapon w = new Weapon();
w.price = 5;//因为Weapon通过extends继承了Item里面的price属性 故其可以直接赋值
}
}
当然,需要注意的是,一个类可以被多个类继承,但一个类只能继承一个类。
1.1.4方法的重载
方法类似C语言的函数
但Java中的方法是可以重名的,即根据传参的不同,来执行同名方法的不同形式
示例代码如下:
public class ADHero extends Hero {
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}
// 可变数量的参数
//这里用了经典重载 由于传参不确定所以用Hero ... heros来表示传入参数
//其中,heros本质是一个数组,因此在后面便利的过程中可以用循环进行输出
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.方法()的形式,将bh作为主参数传入方法中,方法中未对形参进行引用却可进行 //利用的值就是属于bh的属性 例如上面的name
bh.attack(h1);
bh.attack(h1, h2);
}
}
1.1.5构造方法
在C语言中,每一个函数都有返回类型,即使不返回任何值,也会带上void作为标识
而在Java中,有一类方法无返回类型,那么在实例化一个对象的过程中,一定会调用构造方法
且每一个类中只有一个构造方法
示例如下:
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);
}
}
1.1.6this参数
this相当于是中文中的“我”
在任何一句话中,“我”的指向都是不同的。在Java中,this也是这样
因此,在方法中,this就表示类似示例中的h,与传入的参数相区分开
示例如下:
public class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
//参数名和属性名一样
//在方法体中,只能访问到参数name,在这种情况下,是没有办法将传入的参数转给h的
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);
}
}
当碰到构造方法后:
就可以在其中一个的构造方法中调用另一个构造方法
这样有利于代码的后续维护和修改
要注意的是,调用必须写在构造方法开头
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);//这里相当于调用了单个参数的Hero方法
System.out.println("两个参数的构造方法");
this.hp = hp;
}
public static void main(String[] args) {
Hero teemo = new Hero("提莫",383);
System.out.println(teemo.name);
}
}
1.1.7传参
在Java中由于没有指针这个概念,所以不会存在形参和实参的说法。
而在Java中,我们曾经提过,对于基本数据类型,=是赋值的意思,而对于类类型,=又是指向的意思,这里可以类比C语言,即指向是指针的说法,指针传入的东西会一起修改,而赋值传入的东西不会影响实参。
示例如下:
基本数据类型的值:
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);
//这以后,xueping的值仍为100,因为在传入方法的过程中,只是传入了100这个值
System.out.println(xueping);
}
}
类类型传参:(类比指针)
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);
}
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);
//在方法中若hero的值发生改变,则temmo的相应属性值也会发生改变
System.out.println(teemo.hp);
}
}
1.1.8包
包是类的集合,我们可以将不同的类放置在同一个包里。
这里应注意:
1.不同的包里可能有相同名称的类
2.在包1中的某个类中,可能要用到包2中的某个类,因此我们需要导包操作
3.部分类由于使用的太过频繁,系统已经自动帮忙导包了,例如System
示例如下:
package charactor;
//Weapon类在其他包里,使用必须进行import
import property.Weapon;//这就是导包操作,必须要指明是在propervity下的Weapon,不然可能会错误
public class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
//装备一把武器
public void equip(Weapon w){
}
}
1.1.9Java的四种修饰符
这是对于属性的修饰
一共有四种
private 私有的
package/friendly/default 不写
protected 受保护的
public 公共的
示例如下:
private int a = 10;
int a = 10;//(不写就默认是package/friendly/default这种)
protected int a = 10;
public int a = 10;
子类父类是指有继承关系的类
类与类之间的关系:
自身:指类自己
同包子类:指处于同一个包下的子类
不同包子类:指处于不同包下的子类
同包类:是同一个包,但相互之间没有继承关系
其他类:在不同的包内,且之间没有继承关系
使用private修饰属性
自身:是可以访问的
同包子类:不能继承
不同包子类:不能继承
同包类:不能访问
其他包类:不能访问
总结:仅自身可以访问
使用package/friendly/default 并不是要在属性前写明这些,而是不写就默认这些修饰属性
自身:是可以访问的
同包子类:能继承
不同包子类:不能继承
同包类:能访问
其他包类:不能访问
总结:只有同包下可以
使用protected 修饰属性
自身:是可以访问的
同包子类:能继承
不同包子类:能继承
同包类:能访问
其他包类:不能访问
总结:仅其他包类不可以
使用public修饰属性
均可
1.1.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();
}
}
类方法与对象方法的区别:
类方法的定义前面会有static标识(又称作静态方法)
public static void die(){
}
对象方法前面没有static修饰
public void die(){
}
总之,类方法可以访问类属性,对象方法可以访问对象属性
且类方法可以被对象方法和类方法调用,对象方法只能被对象方法或通过对象的类方法调用
1.1.11属性的初始化
属性分为对象属性和类属性
1.对象属性的初始化:
-
声明该属性的时候初始化
-
构造方法中初始化
-
初始化块
示例如下:
package charactor;
public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;
{
maxHP = 200; //初始化块
}
public Hero(){
hp = 100; //构造方法中初始化
}
}
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); } }
类属性的初始化不得使用构造方法,因为其根本没有对象
对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是(静态变量、静态初始化块)>(变量、初始化块)>构造器
对象属性:三个地方的初始化都被被执行,声明初始化和初始化块的顺序在于代码的先后顺序,构造方法的初始化会在最后进行执行。 静态属性:声明初始化和初始化块的顺序在于代码的先后顺序
1.1.12单例模式
在LOL中,大龙只有一条,但若程序员操作失误,就有可能在游戏中出现多条大龙。此时,就需要单例模式,让程序员无论调用多少次单例模式都获得的是同一个东西。
单例模式的三要素:
1.将构造方法私有化
2.将静态属性指向实例
3.在调用时返回2中的静态属性
其中有两种方式:
饿汉式
package charactor;
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance = new GiantDragon();
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance(){
return instance;
}
}
懒汉式
package charactor;
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,用于指向一个实例化对象,但是暂时指向null
private static GiantDragon instance;
//设计另一种方法,未涉及到实例化
public static void PrintXXX(){
System.out.println("XXX");
}
//public static 方法,返回实例对象
public static GiantDragon getInstance(){
//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
if(null==instance){
instance = new GiantDragon();
}
//返回 instance指向的对象
return instance;
}
}
测试代码:
package charactor;
public class TestGiantDragon {
public static void main(String[] args) {
//通过new实例化会报错
// GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象
GiantDragon.PrintXXX();
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
//都是同一个对象
System.out.println(g1==g2);
System.out.println(g1==g3);
}
}
两者的区别在于何时进行实例化
饿汉式是在一开始就进行实例化,而懒汉式是在调用的时候才进行实例化
并且,饿汉式中若系统只调用了别的方法,未涉及到静态变量,其仍然不会实例化化。在上例中表现为若系统单独调用PrintXXX时,是不会进行初始化的
1.1.13枚举类型
枚举类型是区别于类的,即定义时时enum而非class
示例如下:
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
在使用时,也可以使用循环进行列举
示例如下:
1.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;
}
}
}
2.foreach类型
public class HelloWorld {
public static void main(String[] args) {
for (Season a: Season.values())//这里的Season.values相当于是把Season这个枚举类型变为一个数组
{
System.out.println(a);
}
}
}
1.2接口与继承
1.2.1接口的定义
例如LOL中的AP与AD英雄,他们造成伤害的方式是不同的。
那么就需要不同的接口来定义这些伤害。
示例如下:
首先是接口示例:
public interface AD{
public void physicAttack();//这就是一个AD接口
}
public interface AP{
public void physicAttack();//这就是一个AP接口
}
然后我们需要用类去接收他们
下面是类的接收示例:
public class ADHero extends Hero implements AD{//implement就是接收接口
public void physicAttack(){
System.out.println("进行物理攻击");
}
}
public class APHero extends Hero implements AP{
public void magicAttack(){
System.out.println("进行一次魔法攻击");
}
}
//若既有AD伤害又有AP伤害,那么就需要接收两类接口
public class ADAPHero extends Hero implements AD,AP{
public void magicAttack(){
System.out.println("进行一次魔法攻击");
}
public void physicAttack(){
System.out.println("进行物理攻击");
}
}
由上列示例可知,继承只能继承一类,但接口可以接收多个,但是每种接口的方法都需要被再次声明
当一个接口只有一个抽象方法时,它就被称为函数接口
1.2.2对象转型
在Java中有一个概念叫做向上转型:即子类转向父类。因为子类包含了父类的所有方法,当转成父类后,父类中的所有方法都可以在原子类中找到。因此不会存在风险。
向上转型:
当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。
向下转型:
并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。即,Orange,Apple都继承了Fruit,但Orange和Apple是不能相互转换的,只有分别转向Fruit再转回本身。转回本身就是向下转型
但我们要十分注意:
引用本身的类型是不会改变的,改变的只是其指向对象的类型
并且要满足其本身类型是指向对象类型的父类
在强制转换中我们也要注意:
强转过程中的两种类型也要满足子类父类的关系,若为同一父类下的两个子类,是没法相互转型的
示例代码如下:
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;//将右边对象的类型转换为左边引用的类型
//即ADHero当作Hero来用,这当然是可以的
//相当于h指向了一个ADhero类型,但只能用Hero里的属性和方法
}
}
但从父类转向子类时,即向下转型,可能子类中的部分方法是不存在于父类中的,所以存在风险
在这里看一段示例代码:
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;//这里是将h的指向对象类型变为ADHero
ad = (ADHero) h;//这里是先将h的指向对象类型变为ADHero,发现h本身的类型仍然是指向对象类型的父类,再进行下一步,将ad的指向对象类型变为ADHero
h = s;//这里是将h的指向对象类型变为Support
ad = (ADHero)h;//这里是先将h的指向对象类型从Support变为ADHero,发现不可以,就直接报错了
}
}
1.2.3重写机制
即在子类中若有与父类重名的方法,都会默认执行子类所确定的重名方法,这样就可以防止一旦继承父类就限定死所有与父类相同的属性与方法,提高效率
1.2.4多态
多态分为两类:操作符的多态和类的多态
操作符的多态
例如+,在int类型的操作中结果就是int,但若一边是字符串,那么输出的内容也是字符串
示例代码如下:
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.父类引用指向多个子类
2.调用的方法在子类中有重写
示例代码如下:
package property;
public class Item {
String name;
int price;
public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果 ");
}
public static void main(String[] args) {
Item i1= new LifePotion();
Item i2 = new MagicPotion();
System.out.print("i1 是Item类型,执行effect打印:");
i1.effect();
System.out.print("i2也是Item类型,执行effect打印:");
i2.effect();
}
}
1.2.5隐藏
类方法的重写称作隐藏
示例代码如下:
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();
}
}
1.2.6super关键字
super关键字可以和前面的this关键字做对比
this指向的是当前类,而super指向的是父类
super不仅仅可以指向父类的方法,也可指向父类的属性
示例代码铺垫内容:
实例化一个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();
}
}
//子类
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("德莱文");
}
}
1.2.7Object类
Object类是所有类的父类
package charactor;
import property.Item;
public class Hero{
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public static void main(String[] args) {
ADHero ad = new ADHero();
System.out.println(ad instanceof ADHero);
System.out.println(ad instanceof Hero);
System.out.println(ad instanceof Object);
//结果均为true
}
}
1.2.8toString()方法
toString()就是打印包名.类名@虚拟地址
//父类
package charactor;
public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
ADHero h = new ADHero();
h.name = "盖伦";
System.out.println(h.toString());
//直接打印对象就是打印该对象的toString()返回值
System.out.println(h);//打印出来的是包名.类名@虚拟地址
}
}
//由于直接打印toString是没有任何意义的,此时我们就需要将toString在子类中重写
//下面给出重写后的子类代码
package charactor;
public class ADHero extends Hero{
public String toString() {
return super.name;//加入这个后,Hero内打印出来的就是盖伦
}
}
1.2.9finalize()方法
在Java中,若一个对象满足了垃圾回收条件,其并不会立刻回收,要等到虚拟机中的垃圾堆积到一定程度,才会调用finalize()进行集中回收
示例代码如下:
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();
}
}
}
1.2.10equals()方法和==符号
用于比较两个参数是否相等
用法如下
a.equals(b);
在类中,==判断的是两个类是否指向同一个对象。
1.2.11final修饰符
用final修饰的类
该类是不可以被别的类所继承的,这样就可以保证该类的所有方法属性都不会被修改
示例代码如下:
package charactor;
public final class Hero extends Object {
String name; //姓名
float hp; //血量
}
用final修饰的方法
当类中的方法被final修饰后,这个类仍然能被继承,但是子类不能去改变被final修饰的方法,别的便相安无事
示例代码如下:
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修饰的基本变量只有一次赋值的机会,不得进行二次赋值
被final修饰的引用只有一次指向对象的机会,不得进行二次指向
常量
常量是指被public static final修饰的值,其值不会改变,可以被公开直接访问
常量的定义如下
public static final int a = 10;
1.2.12抽象类
1.2.12.1抽象类的定义
抽象类是指在其范围内有抽象方法的类
那么抽象类若作为父类被继承,在子类中,其抽象方法必须被重定义
示例代码如下:
//抽象类作为父类
package charactor;
public abstract class Hero {//抽象类在定义时必须加上abstract
String name;
float hp;
float armor;
int moveSpeed;
public static void main(String[] args) {
}
// 抽象方法attack
// Hero的子类会被要求实现attack方法
public abstract void attack();//加上abstract就是抽象方法
}
//子类示例
package charactor;
public class ADHero extends Hero implements AD {//继承了上述抽象类
public void physicAttack() {
System.out.println("进行物理攻击");
}
@Override
public void attack() {//名为attack的类在抽象类中是作为抽象方法的,因此必须要被重定义
physicAttack();
}
}
1.2.12.2抽象类可以没有抽象方法
抽象类中不一定要有抽象方法
但是一旦是抽象方法,那么在声明时是不能直接实例化的,应当先指向,在后面进行实例化
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.12.3抽象类和接口的区别
区别1:子类只能继承一个类(包括抽象类),但其可以实现多个方法
区别2:抽象类中的别的属性,方法与普通类没有区别,但是接口中所有的属性都是默认public static final的,即常量
接口中的方法都是默认abstract的(即抽象的),且若要在接口中加入实体方法,就要用default修饰
示例代码如下:
package charactor;
public interface AP {
public static final int resistPhysic = 100;
//resistMagic即便没有显式的声明为 public static final
//但依然默认为public static final
int resistMagic = 0;
public void magicAttack();
public default void magicAttack2(){
}
}
1.2.13内部类
内部类有四种
非静态内部类 静态内部类 匿名类 本地类
非静态内部类
顾名思义,就是在普通类中又定义了一个类,且未被static修饰,因此称为非静态内部类
静态内部类的使用方法:
1.声明一个外部类
2.在外部类的基础上声明内部类
3.使用内部类的属性和方法
示例代码如下:
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();
}
}
静态内部类
这是在内部类前用static修饰后得到的
与非静态内部类的最大区别就在于实例化的方法
由上代码可知,非静态内部类若要可以使用,就必须用一个外部类的实例作为基础,而静态内部类则可以直接进行实例化。由于没有外部类作为基础,除了可以访问外部类的私有静态成员外,别的属性与方法都不可访问
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的方法成员罢了
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
crystal.checkIfVictory();
}
}
匿名类
在前面我们讲到,如果在抽象类中定义了一个抽象方法的话,在其子类中,这个抽象方法必须被重写。那么,如果我们在抽象类中实例化其本身,并且在后面直接重写了他的抽象方法,那么我们可以看成我们得到了一个新的类,那么我们就把这个类称作匿名类,且匿存在于是在方法里的。
需要注意的是,在匿名类中使用外部的局部变量时,该局部变量必须要被final修饰
即下方示例中的xxx是不得被修改的
示例代码如下:
package charactor;
public abstract class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public abstract void attack();
public static void main(String[] args) {
int xxx = 5;
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这么一个系统自动分配的类名
xxx = 10;//这句话是错误的,因为xxx的实际类型是 final int
System.out.println(h);
}
}
本地类
本地类是在方法中、循环中被声明的类,即有名字的匿名类。
其与匿名类的区别:
匿名类是在实例化一个类的同时就在内部重写了其抽象方法
其与非静态、静态内部类的区别:
非静态内部类和静态内部类是与该外部类的属性、方法平起平坐的,而本地类一定是实现在方法和循环中的。
示例代码如下:
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();
}
}
1.2.14默认方法
在前面我们提到,所有接口中的方法都要在实现该接口的函数中重写(相当于是抽象方法),但是这样就会非常麻烦,一旦修改,添加了接口中的某种方法,就需要在每个实现该接口的类中去修改。因此就增添了默认方法:用default进行修饰。
示例代码如下:
package charactor;
public interface Mortal {
public void die();
default public void revive() {//这就是默认方法
System.out.println("本英雄复活了");
}
}
当然,你可以重写默认方法,例如:
package charactor;
public class ADHero extends Hero implements Mortal{
public static void main(String [] args) {
System.out.println("开始执行");
}
public void die() {
System.out.print("英雄已死亡");
}
public void revive() {//重写默认方法
System.out.println("复活时回满蓝量");
}
}
这样在执行的时候就是执行的重写部分
1.2.15UML图
UML图中
斜体代表抽象
正体代表普通
由上到下的三个框中分别是类名-属性名-方法名
1.3数字与字符串
1.3.1装箱与拆箱
在Java中,每一个基本类型都有对应的类类型。例如int的类类型就是Integer。
在这里,如果需要完成基本类型和类类型的转化,就可以直接利用装箱和拆箱(否则就需要用到强转等等)
自动将基本类型转化为类类型就是装箱,反之就是拆箱
示例代码如下:
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//自动转换就叫装箱
Integer it2 = i;
}
}
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
//自动转换就叫拆箱
int i3 = it;
}
}
1.3.2字符串与数字之间的相互转换
数字转字符串
示例代码如下:
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();
}
}
字符串转数字
示例代码如下:
package digit;
public class TestNumber {
public static void main(String[] args) {
String str = "999";
int i= Integer.parseInt(str);
System.out.println(i);
}
}
1.3.3常用数学方法
常用的数学方法有四舍五入 随机浮点数 随机整数 开方 次方 π 自然常数e
示例代码如下:
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);
}
}
1.3.4格式化输出
如果java的输出只根据我们以前用的用+相连,那与C语言等相比较就太过复杂了。因此,java提供了格式化输出这种方法。
示例代码如下:
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";
//使用printf格式化输出
System.out.printf(sentenceFormat,name,kill,title);
//使用format格式化输出
System.out.format(sentenceFormat,name,kill,title);
//format和printf的作用是一模一样的
}
}
接下来介绍一些java的常用格式化符号
总长度,左对齐,补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);
}
}
1.3.5字符
char就是和C语言中一样的字符型
其对应的封装类为character
示例代码如下:
package character;
public class TestChar {
public static void main(String[] args) {
char c1 = 'a';
Character c = c1; //自动装箱
c1 = c;//自动拆箱
}
}
其常用方法如下:
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");
}
}
1.3.6字符串
字符串是Java中运用最多的类型之一,但其不是基本数据类型。
1.3.6.1字符串的定义方式
有三种定义方式
示例代码如下:
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;// 通过+加号进行字符串拼接
}
}
1.3.6.2字符串的final类型
String类型都被自动修饰为final,因此String是不得被继承的
示例代码如下:
package character;
public class TestString {
public static void main(String[] args) {
MyString str = new MyString();
}
/*这里会报错,因为String不能被继承*/
static class MyString extends String{
}
}
1.3.6.3immutable
immutable是指字符串是不得被修改的,即使是字符串的拼接,也是要引用新的String类型指向拼接后的字符串的,可类比常量
1.3.6.4字符串的格式化输出
由前面的内容可知,我们在输出的时候为了方便可以采用格式化输出。而字符串内包含了一个自己的format。即可以先创建一个字符串接收完要输出的内容,然后再输出。
示例代码如下:
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);
}
}
1.3.6.5字符串的长度问题
首先要明确,空串和长度为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());
//但如果定义为null
String xxx = null;
System.out.println(xxx);//会报错
}
}
1.3.6.6字符串的常见方法
1.charAt()
str.charAt(x)就是指取到str字符串的第x位字符
2.toCharArray()
这是把字符串转化为字符数组
示例代码如下
String str = "abcdefg";
char [] cs = str.toCharArray();
System.out.println((str.length() == cs.length));
3.substring()
substring()有两种取法
(1)str.substring(x)是指从str的第x位(从0开始)开始取(包括x)到末尾
(2)str.substring(x,y)是指从str的第x位(从0开始)开始取(包括x)到str的第y位(不包括y)
示例代码如下:
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//截取从第3个开始的字符串 (基0)
//打印 在进行了连续8次击杀后,获得了 超神 的称号
String subString1 = sentence.substring(3);
System.out.println(subString1);
//截取从第3个开始的字符串 (基0)
//到5-1的位置的字符串
//左闭右开
//打印 在进
String subString2 = sentence.substring(3,5);
System.out.println(subString2);
}
}
4.分隔
分隔的方法是str.split(“x”) 其中str是字符串,x是分割符
示例代码如下:
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);
}
}
}
5.去掉首尾空格
str.trim()就用于去除字符串的首尾空格
示例代码如下:
package character;
public class TestString {
public static void main(String[] args) {
String sentence = " 盖伦,在进行了连续8次击杀后,获得了 超神 的称号 ";
System.out.println(sentence);
//去掉首尾空格
System.out.println(sentence.trim());
}
}
6.大小写转换
str.toLowerCase()
str.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());
}
}
7.定位
str.indexOf(‘x’) 或 str.indexOf(“ab”) 这是定位x或ab在这个字符串中首次出现的位置
若要求最后出现的位置,就要使用str.lastindex()
同样的,若要从第num的位置开始查询x字符出现的位置,就用str.indexOf(‘x’, num)
若要验证x是否存在于该字符串中,则用str.contains(‘x’)
示例代码如下:
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开始(包括位置5),出现的第一次,的位置
System.out.println(sentence.contains("击杀")); //是否包含字符串"击杀"
}
}
8.替换
replaceAll(“x”,“y”) 替换所有的 用y替换x
replaceFirst(“x”,“y”) 只替换第一个
示例代码如下:
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
String temp = sentence.replaceAll("击杀", "被击杀"); //替换所有的
temp = temp.replaceAll("超神", "超鬼");
System.out.println(temp);
temp = sentence.replaceFirst(",","");//只替换第一个
System.out.println(temp);
}
}
1.3.7比较字符串
在Java中,用两种比较字符串的方式
1.== 用于判断引用是否指向同一个对象
在这里有一个特殊情况,即字面值一样的字符串,编译器会自动调用前面那个
2.str.equals(x)用于判断内容是否一样
示例代码如下:
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);//false
}
}
//特例
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String str3 = "the light";
System.out.println( str1 == str3);//true
}
}
//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(x) & endsWith(x)
示例代码如下:
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));//以...结束
}
}
1.3.8StringBuffer
StringBuffer就是长度可以改变,各种增删改查操作都可以进行的字符串。
以下操作都只能针对StringBuffer对象
1.3.8.1 追加 删除 插入 反转
append(x) & delete(x,y) & insert(x,y) & 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);
}
}
1.3.8.2长度 容量
length() & capacity()
示例代码如下:
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());//总空间
}
}
1.4日期
1.4.1Date类
1.4.1.1Date的创建
java中的日期类型是以1970年1月1日8:00为开始的(8:00是因为中国为太平洋时区)
日期的使用必须进行导包:
import java.util.Date;
创建Date对象就可以知道日期
Date()是创建当前时间
Date(x)是从1970年1月1日8:00向后推x毫秒
示例代码如下:
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);
}
}
1.4.1.2Date.getTime()和System.currentTimeMills()
两者获得的内容其实是一样的,是从1970年1月1日 早上8点0分0秒 开始经历的毫秒数
示例代码如下:
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());
}
}
1.4.2日期格式化
1.4.2.1日期转字符串
即将Date的输出变得更加可观变为yyyy-MM-dd HH:mm:ss SSS
这也需要导包
import java.text.SimpleDateFormat;
import java.util.Date;
且需要用到SimpleDateFormat()
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
示例代码如下:
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);
}
}
1.4.2.2字符串转日期
即将上述过程反方向执行
需要导包
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
这里需要安全检验,因为系统需要判断你给的字符串格式是否正确
然后再进行转换
Date d = sdf.parse(str);
示例代码如下:
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();
}
}
}
1.4.3Calendar
具体方法都看示例代码
示例代码如下:
Calendar与Date进行转换
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();//一个建立Calendar引用
//通过日历对象得到日期对象
Date d = c.getTime();
Date d2 = new Date(0);
c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00
}
}
翻日历
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);
}
}