Markdown

标题

#+空格+ 一级标题

字体

粗体 %两边两个*

斜体 %两边一个*

斜体+加粗 %两边三个*

删除线 %两边两个~

引用

文本引用效果 %用>+内容

分割线



%三个- 或者三个~

图片

% ! + [] + ()

超链接

点击跳转

%[] + ()

列表

  1. A

  2. B %数字+点+空格

  • A
  • B %减号+空格

表格

名字|性别|生日

–|--|–|

张三|男|7.2 %源代码模式删除空格

名字 性别 生日
张三 7.2

代码

public

%三个`

预科

冯诺依曼体系结构

系统软件

DOS(磁盘操作系统)、windows、Linux、Unix、Mac、Android、iOS

快捷键

alt + F4 %关闭当前窗口

win + E %打开我的电脑

win + D %最小化所有页面

DOS

  1. win + R 输入cmd
  2. 任意文件夹下面,按住shift+鼠标右键,打开powershell
  3. 资源管理器地址栏前面加cmd
 D: %盘符+冒号进入磁盘dir %显示目录cd + 地址 %change directory进入cd + /d + D:\电影 %跨盘符切换cd.. %返回上一级cls %clean screen清理屏幕ipconfig %查看电脑ip config配置#打开应用calcmspaintnotepad#ping 命令ping www.google.com#创建文件夹md#文件cd 空格 >a.txtdel a.txtrd

摩尔定律

第三代语言

C语言时典型的面向过程的语言,C++、Java时典型的面向对象的语言。

Java入门

高可用 高性能 高并发

特性

简单性、面向对象、可移植性、高性能、分布式、动态性、多线程、安全性、健壮性

跨平台

write once、run anywhere

版本

JavaSE:标准版(桌面程序,控制台开发)

JavaME:嵌入式开发(几乎淘汰)

JavaEE:企业级开发(web端,服务器开发)

JDK、JRE、JVM

JDK:Java Development Kit %开发者工具箱

JRE:Java Runtine Environment %运行环境

JVM:Java Virtual Machine %虚拟机

安装开发环境

  1. 配置环境变量JAVA_HOME
  2. 配置path变量

测试安装成功

cmd–> java -version

目录

bin:可执行程序

include:引入C语言中的一些头文件

jre:运行环境

lib:库文件

src:资源文件 类

hello world

记事本编辑editplus配置:关闭文件类型及语法自动完成;关闭自动保存创建备份

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

编译javac ().java

运行java ()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olU8pZWX-1633015148278)(F:\学习\code\hello.png)]

Java程序运行机制

compile编译型 解释型

IDEA

集成开发环境IDE(Integrated Development Environment ):用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。

Java基础

注释

  1. 单行注释://
  2. 多行注释:/* ··· */
  3. 文档注释:/** … */

有趣的代码注释

标识符

Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。

标识符以字母、美元符$、下划线开始。首字符之后可以是任意组合(字母、下划线、美元符、数字)。(变量可以中文)

大小敏感。

关键字列表:

数据类型

强类型语言:要求变量使用严格符合规定,所有变量必须先定义才使用。

弱类型语言:VB、JS

八大基本数据类型

byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-2^7 ~ 2^7-1(-128~127),默认值0

short:短整型,在内存中占16位,即2个字节,取值范围-2^15 ~ 2^15-1(-32768~32767),默认值0

int:整型,用于存储整数,在内在中占32位,即4个字节,取值范围-2^31 ~ 2^31-1 (-2147483648~2147483647)约21亿,默认值0

long:长整型,在内存中占64位,即8个字节-263~263-1,默认值0L

Java语言的整型常数默认为int型,声明long型常量可以后加‘ l ’或‘ L ’ 。

float:浮点型,在内存中占32位,即4个字节,用于存储带小数点的数字(与double的区别在于float类型有效小数点只有6~7位),默认值0

double:双精度浮点型,用于存储带有小数点的数字,在内存中占64位,即8个字节,默认值0

注意: 默认的小数值是double类型的

注意: 默认的小数值是double类型的
所以 float f = 54.321会出现编译错误,因为54.321的默认类型是 double,其类型 长度为64,超过了float的长度32
在数字后面加一个字母f,直接把该数字声明成float类型
float f2 = 54.321f,
这样就不会出错了

当以f或者F结尾的时候,就表示一个float类型的浮点数,否则就是double类型(以d或者D结尾,写不写都可以)。
浮点数还可以用E或者e表示(科学计数法)
e2表示10的二次方,即100
1.234e2 = 1.234x100

char:字符型,用于存储单个字符,占16位,即2个字节,取值范围0~65535,默认值为空

字符的字面值放在单引号中

字符串的字面值放在双引号中

需要注意的是,\表示转义,比如需要表示制表符,回车换行,双引号等就需要用 \t \r \n " 的方式进行

 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 = '\\'; //反斜杠}
}

boolean:布尔类型,占1个字节,用于判断真或假(仅有两个值,即true、false),默认值false

byte short int long
float double
char
boolean
public class Helloword {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+val);}}

引用类型

String:String类型其实并不是基本类型,但是它是如此广泛的被使用,常常被误以为是一种基本类型。
String类型是Immutable的,一旦创建就不能够被改变

接口

数组


数据类型拓展

System.out.println(Integer.toBinaryString(i2)); //查看一个整数对应的二进制的方法

整数拓展:二进制0b、十进制、八进制0、十六进制0x(0-9,A-F)

浮点数拓展:float有限、离散、舍入误差、大约、接近但不等于(最好不要使用float);银行常用BigDecimal

System.out.println((int)c); //强制转码

所有字符本质还是数字

编码Unicode表:(97=a、65=A) 2字节 0-65536(2^16)

转义字符

字符 作用
\n 换行 \n 换行符,使光标定位到下一行。
\r 回车 \r 回车符,使光标回到当前行的行首。如果之前该行有内容,则会被覆盖;
\t 制表 (相当于tab)
\f 换页

\t 制表符(相当于键盘上按下TAB键之间的距离,一般为8个空格。)

使用制表符是把输出的切入点移动到下一个能被8整除的位置上。
即当打印小于八格的结果,用空格补足八格再打印下一个结果;当大于等于八格,小于十六格,补足十六格;以此类推。

\n换行符

public class text02 {public static void main(String[] args) {System.out.println("1234567 12345678 0.1234567 0.12345678");System.out.println("1234567\t12345678\t0.12345\t0.12345678");System.out.println("1234567 \n12345678\n0.1234567\n0.12345678");}
}

类型转换

小数的优先级大于整数

byte,short,char->int->long->float->double

//强制转换 (类型)变量名 高到低 要避免内存溢出

//自动转换 低到高

运算时如果有一个参数是L或者double,结果位L或者double

运算时如果比int低,结果均为int

注意

  1. 不能对布尔值转换
  2. 不能把对象类型转换为不相干类型
  3. 高到低时,强制转换
  4. 可能出现内存溢出或者精度问题

int money=10_0000_0000; JDK7新特性,数字之间可以用下划线分割

变量

type varName [=value] [{,varName[=value]}];//数据类型 变量名 =值;可以使用逗号隔开来声明多个同类型变量。

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

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

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

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

变量作用域

  1. 类变量:从属类
  2. 实例变量:在类里,从属于对象,如果不初始化,会变成默认值(基本类型0 0.0 Boolean默认false,此外都是null
  3. 局部变量:在方法内,必须声明和初始化
public class Variable{static int salary = 2500; //类变量String str = "hello"; //实例变量int age;public void method(){int i = 0; //局部变量System.out.println(i);//实例变量 变量类型 变量名字 = new Variable();Variable variable = new Variable();System.out.println(variable.str); //输出nullSystem.out.println(variable.age); //输出0//类变量 staticSystem.out.println(salary);}
}

常量

初始化(initialize)后不能再改变值

final 常量名=值;//一般使用大写字母

如果在声明的时候未赋值,那么可以在后面代码进行唯一的一次赋值

inal 除了修饰变量,还可以修饰类,修饰方法

final修饰的类不能被继承 final定义的方法不能被重写 final定义的常量不能被重写赋值

final int[] a = {1,2,3} 这种栈里a变量空间存放的是堆内存的地址。地址是不可变的,但是数组里的值是可以变

final 修饰的变量在方法中,可以先初始化再赋值。 但是如果是全局变量,必须在初始化的时赋值,不然会报错。

当final修饰形参时,不能在方法里赋值,因为在调用方法的时候,就一定会第一次赋值了,后面不能再进行多次赋值

public class text03 {//修饰符,不存在先后顺序final static double PI = 3.14;//static final double PI = 3.14;public static void main(String[] args) {System.out.println(PI);}
}

表达式

表达式是由变量、操作符以及方法调用所构成的结构。

; 也是一个完整的表达式

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

public class HelloWorld { //类对应的块public static void main(String[] args) { //主方法对应的块System.out.println("abc");}
}

命名规范

类成员变量:首字母小写和驼峰原则:monthSalary 除了第一个单词以外,后面的单词首字母大写

局部变量:首字母小写和驼峰原则

常量:大写字母和下划线:MAX_VALUE

类名:首字母大写和驼峰原则:GoodMan

方法名:首字母小写和驼峰原则:runRun()

运算符

算术运算符:+、-、*、/、%(模运算,取余)、++、–

赋值运算符:=

关系运算符:>、<、>=、<=、==、!=、instanceof

逻辑运算符:&&、||、!

& && 长路与 和 短路与
| || 长路或 和 短路或
! 取反
^ 异或^
public static void main(String[] args){//长路与  无论第一个表达式的值是true或者false,第二个的值,都会被运算
//短路与 只要第一个表达式的值是false的,第二个表达式的值,就不需要进行运算了//长路或  无论第一个表达式的值是true或者false,第二个的值,都会被运算
//短路或 只要第一个表达式的值是true的,第二个表达式的值,就不需要进行运算了
int i = 2;
System.out.println( i== 1 | i++ ==2  ); //无论如何i++都会被执行,所以i的值变成了3
System.out.println(i);}

位运算符:&、|、^、~、>>、<<、>>>(了解)

Integer.toBinaryString() 一个整数的二进制表达
| 位或
& 位与
^ 异或
~ 取非
<< >> 左移 右移
>>> 带符号右移与无符号右移
public class HelloWorld {public static void main(String[] args) {int i  =5;int j = 6;System.out.println(Integer.toBinaryString(i)); //5的二进制是 101System.out.println(Integer.toBinaryString(j)); //6的二进制是110System.out.println(i^j); //所以 5^6 对每一位进行或运算,得到 011->3System.out.println(i^0); //任何数和自己进行异或 都等于 0System.out.println(i^i); //任何数和0 进行异或 都等于自己}
}

条件运算符 ?、:

扩展赋值运算符:+=、-=、*=、/=

Ctrl+D 在IDEA中复制当前行到下一行

//运算时如果有一个参数是L或者double,结果位L或者double
//运算时如果比int低,结果均为int,包括char
public class Text04 {public static void main(String[] args) {long a = 123456789L;int b = 12356;short c = 100;byte d = 5;double e = 3.14;char f = 'g';System.out.println(a+b);//LongSystem.out.println(b+c);//IntSystem.out.println(c+d);//IntSystem.out.println(d+e);//DoubleSystem.out.println(d+f);//Int}
}

一元运算符
int b = a++; //执行完这段代码后,先给b赋值,再自增
int c = ++a; //执行完这段代码前,先自增,再赋值

幂运算需要使用工具类

Math.pow(3,2); //9

逻辑与会造成短路运算

public class Text05 {    public static void main(String[] args) {        boolean a = true;        boolean b = false;        System.out.println("a && b: "+ (a && b)); //逻辑与运算:两个都为真,结果真;若第一个为假,会造成短路运算System.out.println("a || b: "+ (a || b));        System.out.println("!(a && b): "+ !(a && b));        //短路运算        int c = 5;        boolean d = (c<4) && (c++<4);        System.out.println(d);        System.out.println(c); //5    }
}

位运算是对二进制位的运算

A&B 与

A|B 或

A^B 异或(相同为0,不同为1)

~B 非

最快速度运算2*8(位运算,效率高)

0000 0000    00000 0001    10000 0010    20000 0100    40000 1000    8

<< 左移 (*2) >>右移(/2)

>>:带符号右移。正数右移高位补0,负数右移高位补1。比如:

4>>1,结果为2;

-4>>1,结果为-2.

>>>:无符号右移。无论正数还是负数,高位通通补0.

对于正数而言,>>和>>>没有区别。

负数转换为二进制,就是将其相反数(正数)的补码的每一位变反(1变0,0变1)最后将变完了的数值加1,就完成了负数的补码运算。这样就变成了二进制。

对于负数而言,-2>>>1,结果是2147483647(Integer.MAX_VALUE)

-1>>>1,结果是2147483647(Integer.MAX_VALUE)

a+=b; //a=a+b

a-=b; //a=a-b

+=即自加
i+=2;
等同于
i=i+2;
其他的 -= , *= , /= , %= , &= , |= , ^= , >>= , >>>= 都是类似

public class Text06 {    public static void main(String[] args) {        int a = 10;        int b = 20;        //字符串连接符 + , String        System.out.println(a+b); //30        System.out.println(""+a+b); //1020        System.out.println(a+b+""); //30    }
}

三元运算符 条件运算符

x ?y :z //如果x==ture,则结果为y,否则结果为z

public class Text07 {    public static void main(String[] args) {        int score = 80;        String type = score < 60 ? "不及格" : "及格";        System.out.println(type);    }
}

优先级

Java 语言中运算符的优先级共分为 14 级,其中 1 级最高,14 级最低。在同一个表达式中运算符优先级高的先执行。

**结合性:**当一个运算对象两侧的运算符优先级别相同时,则按运算符的结合性来确定表达式的运算顺序。

优先级 运算符 结合性
1 ()、[]、{} 从左向右
2 !、+、-、~、++、– 从右向左
3 *、/、% 从左向右
4 +、- 从左向右
5 «、»、>>> 从左向右
6 <、<=、>、>=、instanceof 从左向右
7 ==、!= 从左向右
8 & 从左向右
9 ^ 从左向右
10 | 从左向右
11 && 从左向右
12 || 从左向右
13 ?: 从右向左
14 =、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>= 从右向左

包机制

包的本质就是一个文件夹

一般使用公司域名倒置作为包名:com.baidu.www

为了使用某一个包的成员,我们需要再Java程序中明确导入该包。使用“import”语句可以完成此功能。

import package1.package2.classname;

import package1.package2.*; //导入这个包下所有的类

JavaDoc(/**… */)

加在类上面就是类的注释,加在方法上面就是方法的注释

@author

@version 版本号

@since 指明需要最早使用的jdk版本

@param 参数名

@return 返回值情况

@throws 异常抛出情况

打开cmd,进入该类所在的目录,然后输入

javadoc -encoding UTF-8 -charset UTF-8 Text08.java

  • -encoding UTF-8:表示设置编码。
  • -charset UTF-8:也表示设置编码。

IDEA

Java流程控制

Scanner对象

注意: 使用Scanner类,需要在最前面加上

import java.util.Scanner;

实现程序和人的交互,java.util.Scanner是Java5的新特征,可以通过Scanner类来获取用户的输入。

Scanner s = new Scanner(System.in);

通过Scanner类的next()与nextLine()方法获取输入的字符串

String str = scanner.nextLine();

读取前,需要使用hasNext()与hasNextLine()判断是否还有输入数据。

凡是属于IO流的类如果不关闭会一直占用资源 即输入输出流

scanner.close();

package com.wang.operator;import java.util.Scanner;public class Demo01 {public static void main(String[] args) {//创建一个扫描器对象,用于接收键盘数据Scanner scanner = new Scanner(System.in);System.out.println("使用next方式接收:");//判断用户有没有输入字符串if(scanner.hasNext()){//使用next方式接收String str = scanner.next();System.out.println("输入的内容为:"+str); //+为连接符}//凡是属于IO流的类如果不关闭会一直占用资源 即输入输出流scanner.close();}
}

next()读取到空格结束;

nextLine()读取到enter结束;

next() 与 nextLine() 区别

next():

  • 1、一定要读取到有效字符后才可以结束输入。
  • 2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
  • 3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
  • next() 不能得到带有空格的字符串。

nextLine():

  • 1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
  • 2、可以获得空白。

需要注意的是,如果在通过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);}
}

判断是否是整数与小数数据

package com.wang.operator;import java.sql.SQLOutput;
import java.util.Scanner;public class Demo02 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in); //new Scanner(System.in);int i = 0;float f = 0.0f;System.out.println("请输入整数:");if (scanner.hasNextInt()){i =scanner.nextInt();System.out.println("整数数据:"+i);}else{System.out.println("输入的不是整数数据!");}System.out.println("请输入小数:");if (scanner.hasNextFloat()){f =scanner.nextFloat();System.out.println("小数数据:"+f);}else{System.out.println("输入的不是小数数据!");}scanner.close();}
}

输入多个数字,求和与平均值,enter确认,非数字结束并输出

package com.wang.operator;import java.util.Scanner;public class Demo03 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);double sum = 0;int m = 0;while(scanner.hasNextDouble()){double x = scanner.nextDouble();m++;sum = sum + x;System.out.println("输入第"+m+"个数据,当前结果sum="+sum);}System.out.println(m+"个数的和为"+sum);System.out.println(m+"个数的平均值为"+(sum/m));scanner.close();}
}

顺序结构

顺序执行,任何算法的基本结构。

选择结构

//如果只有一个表达式可以不用写括弧,看上去会简约一些

     if(b){System.out.println("yes1");}if(b)System.out.println("yes1");

在第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");}
}
  1. if单选择结构
  2. if双选择结构
  3. if多选择结构
  4. 嵌套的if结构
  5. switch多选择结构:判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
package com.wang.operator;import java.util.Scanner;public class Demo04 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("输入内容");String s = scanner.nextLine();//判断字符是否相等if (s.equals("Hello")){System.out.println(s);}else{System.out.println("不一致");}System.out.println("End");scanner.close();}
}

switch多选择结构:判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。

变量类型可以是byte、short、int、char、String

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

case标签必须为字符串常量或者字面量

case穿透:不加break就全输出

switch(expression){case value: break;default:}

package com.wang.operator;public class Demo05 {public static void main(String[] args) {String name = "wang";switch (name){case "王":System.out.println("王");break;case "wang":System.out.println("wang");break;default:System.out.println("error");}}
}

反编译 查看class文件 ctrl+alt+shift+s打开项目结构

while循环结构

while(布尔表达式){

// 循环内容

}

我们大多数情况会让循环停止下来,需要一个让表达式失效的方式来结束循环。

**死循环:**while(ture){} / /等待客户端连接、定时检查

do…while循环(先执行后判断)和while(先判断后执行)相似,不同在于do…while循环至少执行一次。

package com.wang.operator;public class Demo06 {public static void main(String[] args) {int a = 0;while (a < 0){System.out.println(a);a--;}System.out.println("===============");do{System.out.println(a);a--;}while(a<0 && a>-10);}
}

for循环结构

最有效、最灵活的循环结构(100.for+enter自动生成语句)

for(初始化;布尔表达式;更新){

//代码语句

}

死循环 for( ; ; ){}

// 计算0到100之间奇数与偶数和
package com.wang.operator;public class Demo07 {public static void main(String[] args) {int oddSum = 0;int evenSum = 0;for (int i = 0; i < 100; i++) {if (i%2!=0){oddSum+=i;}else{evenSum+=i;}}System.out.println("奇数和:"+oddSum+"\n"+"偶数和:"+evenSum);}}

print 输出完不会换行

println输出完会换行

//输出5的倍数,每行输出3个
package com.wang.operator;public class Demo08 {public static void main(String[] args) {for(int i=1; i<=1000 ; i++){if(i%5==0){System.out.print(i+"\t");}if(i%15==0){System.out.println("\n");}}}
}
//打印九九乘法表
package com.wang.operator;public class Demo09 {    public static void main(String[] args) {        for(int i=1;i<=9;i++){            for(int j=1;j<=9;j++){                if(j<=i){                    System.out.print(j+"*"+i+"="+(j*i)+"\t");                }else{                    System.out.println();                    break;                }            }        }    }
}
//利用j<=i的动态变化来优化
package com.wang.operator;public class Demo09 {    public static void main(String[] args) {        for(int i=1;i<=9;i++){            for(int j=1;j<=i;j++){                    System.out.print(j+"*"+i+"="+(j*i)+"\t");            }            System.out.println();        }    }
}

增强for循环:主要用于数组或集合的增强for循环。

for(声明语句:表达式){

//代码句子

}

package com.wang.operator;public class Demo10 {    public static void main(String[] args) {        int[] numbers = {10,20,30,40,50,60}; //定义了一个数组        for (int i=0;i<=5;i++){            System.out.println(numbers[i]);        }        System.out.println("========================");        //遍历数组的元素        for (int x:numbers){            System.out.println(x);        }    }
}

break、continue、goto

break用于强行退出循环,不执行循环中剩余的语句(可在switch中使用)

直接结束当前for循环

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

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

**标签:**指后面跟一个冒号的标识符,如:label:

package com.wang.operator;public class Demo11 {    public static void main(String[] args) {        //打印101-150之间所有的质数        //带标签的continue        int count = 0;        outer:for (int i=101;i<=150;i++){            for (int j=2;j<=i/2;j++){                if (i%j==0){                    continue outer;                }            }            System.out.print(i+" ");        }    }
}

使用标签结束外部循环

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

public class HelloWorld {public static void main(String[] args) {//打印单数    outloop: //outloop这个标示是可以自定义的比如outloop1,ol2,out5for (int i = 0; i < 10; i++) {for (int j = 0; j < 10; j++) {System.out.println(i+":"+j);if(0==j%2) break outloop; //如果是双数,结束外部循环}}}
}

借助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; //终止外部循环的标记设置为truebreak;}}if (breakout) //判断是否终止外部循环break;}}
}

Java方法

一个方法只完成1个功能,这样有利于后期扩展。

ctrl+/注释

//形式参数,用来定义作用的

//实际参数,实际调用传递给他的参数

修饰符 返回值类型 方法名(参数类型 参数名){

方法体

return 返回值;

}

值传递和引用传递: Java是值传递

//输入两个整数比较大小
package com.wang.method;import java.util.Scanner;public class Demo01 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int num1 =0;int num2 =0;System.out.println("输入第一个整数:");if (scanner.hasNextInt()) {num1 = scanner.nextInt();}else{System.out.println("输入的不是整数数据");}System.out.println("输入第二个整数:");if (scanner.hasNextInt()) {num2 = scanner.nextInt();}else{System.out.println("输入的不是整数数据");}int result = max(num1,num2);if (result!=0) {System.out.println("更大的数为:" + result);}else{System.out.println("两数相等!");}}public static int max(int num1,int num2){int result = 0; //return要提取到最外面if (num1==num2){return 0; //终止方法}if (num1<num2){result=num2;}else{result=num1;}return result;}
}

重载

在一个类中,有相同的函数名称,但形参不同的函数。

方法名称相同时,编译器根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。

规则:参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。

命令行传参

有时候希望运行一个程序时再传递给它消息。这要靠传递命令行参数给main()函数实现。

可变参数

在方法声明中,在指定参数类型后加一个省略号(…)。

一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。(不定项参数)

package com.wang.method;public class Demo02 {public static void main(String[] args) {//调用可变参数Demo02 demo02 = new Demo02();demo02.pringMax(12,13,2,3,4);}public void pringMax(double... numbers){ //在方法里,使用操作数组的方式处理参数 numbers 即可if (numbers.length == 0){System.out.println("No argument passed");return;}double result = 0;//排序for(int i=1;i< numbers.length;i++){if (numbers[i]>result){result=numbers[i];}}System.out.println("The max value is "+result);}
}

递归

A方法调用A方法。

递归结构:

  1. 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
  2. 递归体:什么时候需要调用自身方法。
package com.wang.method;public class Demo03 {//阶乘public static void main(String[] args) {System.out.println(f(8));}public static int f(int n){if (n==1){return 1;}else{return n*f(n-1);}}
}

Java使用栈机制,递归会调用大量的函数,可能导致内存崩溃

计算器(结果尚未未实现整数输出功能)

写一个计算器,要求实现加减乘除功能,并且能够循环接受新的数据,通过用户交互实现

  • 写四个方法:加减乘除
  • 利用循环+switch进行用户交互
  • 传递需要操作的两个数
  • 输出结果
package com.wang.method;import java.util.Scanner;public class Demo04 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);double num1 =0;double num2 =0;System.out.println("开始计算!");if(scanner.hasNextInt()){num1= scanner.nextInt();}else{if(scanner.hasNextDouble()) {num1 = scanner.nextDouble();}else {System.out.println("输入一个数!");}}String c="";if(scanner.hasNext()){c= scanner.next();}if(scanner.hasNextInt()){num2= scanner.nextInt();}else{if(scanner.hasNextDouble()) {num2 = scanner.nextDouble();}else {System.out.println("输入一个数!");}}switch (c){case "+":System.out.println(num1+"+"+num2+"="+add(num1,num2));break;case "-":System.out.println(num1+"-"+num2+"="+sub(num1,num2));break;case "*":System.out.println(num1+"*"+num2+"="+mul(num1,num2));break;case "/":System.out.println(num1+"/"+num2+"="+div(num1,num2));break;default:System.out.println("不正确的运算符");}}//加public static int add(int num1,int num2){int result = num1 +num2;return result;}public static double add(double num1,double num2){double result = num1 +num2;return result;}//减public static int sub(int num1,int num2){int result = num1 -num2;return result;}public static double sub(double num1,double num2){double result = num1 -num2;return result;}//乘public static int mul(int num1,int num2){int result = num1 *num2;return result;}public static double mul(double num1,double num2){double result = num1 *num2;return result;}//除public static int div(int num1,int num2){if (num2==0) {System.out.println("error");return 0;}else{int result = num1 / num2;return result;}}public static double div(double num1,double num2){if (num2==0) {System.out.println("error");return 0;}else{double result = num1 / num2;return result;}}}

数组

数组是相同类型数据的有序集合。每个数组元素可以通过一个下标来访问他们。

声明数组变量,才能在程序中使用数组。

dataType[] arrayRefVar; //声明一个数组。首选的方法、int[] nums;
dataType arrayRefVar[]; //效果相同,但不是首选方法(C和C++首选)nums = new int[10]; //创建一个数组

Java语言使用new操作符来创建数组:

dataType[] arrayRefVar = new dataType[arraySizw]; //int[] nums = new int[10];

获取数组长度:

array.length

创建数组的时候,要指明数组的长度。
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]; //声明的同时,指向一个数组}
}

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

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

内存分析

堆://创建数组、赋值

  1. 存放new的对象和数组

  2. 可以被所有的线程共享,不会存放别的对象引用

栈://声明数组

  1. 存放基本变量类型(会包含这个基本类型的具体数值)
  2. 引用对象的变量(会存放这个引用在堆里面的具体地址)

方法区:

  1. 可以被所有的线程共享
  2. 包含了所有的class和static变量

三种初始化

如果指定了数组的内容,就不能同时设置数组的长度。

  1. 静态初始化:创建+赋值
int[] a = {1,2,3,4,5,6,7,8,9};
Man[] mans = {new Man(1,1),new Man(2,2)};
  1. 动态初始化:
int[] a = new int[2];
a[0]=1;
a[1]=2;
  1. 数组的默认初始化:数组是引用类型,他的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

四个基本特点

  1. 长度确定
  2. 元素类型相同,不允许混合
  3. 元素可以是任何数据类型,包括基本类型和引用类型
  4. 数组变量属引用类型,数组可看成对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。

数组边界

合法区间:[0,length-1]

ArrayIndexOutOfBoundsException:数组下标越界异常!

package com.wang.array;public class Demo01 {public static void main(String[] args) {int[] nums = new int[10];nums[0] = 1;for (int i = 0; i < nums.length ; i++) {System.out.print(nums[i]+" ");}System.out.println(nums[10]);for (int num :nums){ // nums.forSystem.out.println(nums); //适合遍历输出,但是取不到下标}}
}

使用方法打印与反转整数与小数类型数组

package com.wang.array;public class Demo02 {public static void main(String[] args) {int[] arrays = {1,2,3,4,5};double[] arrays2 = {1.1,2.2,3,4,5};int[] reverse = reverse(arrays);double[] reverse2 = reverse(arrays2);printArray(reverse);printArray(reverse2);}//打印数组public static void printArray(int[] arrays){for (int array : arrays) {System.out.print(array+" ");}System.out.println("");}public static void printArray(double[] arrays){for (double array : arrays) {System.out.print(array+" ");}System.out.println("");}//反转数组public static int[] reverse(int[] arrays){int[] result = new int[arrays.length];for(int i=0, j=result.length-1; i < arrays.length;i++,j--){result[j]=arrays[i];}return result;}public static double[] reverse(double[] arrays){double[] result = new double[arrays.length];for(int i=0, j=result.length-1; i < arrays.length;i++,j--){result[j]=arrays[i];}return result;}
}

多维数组

多维数组可以看出是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。

二维数组:

int a[][] = new int[2][5]; //两行五列

Java中使用多维数组不多,主要是面向对象!

package com.wang.array;public class Demo03 {public static void main(String[] args) {int[][] a ={{1,2},{1,2,3},{1},{1,2}};System.out.println(a.length); //几行System.out.println(a[1].length); //二行几列for (int i=0; i<a.length;i++){ //遍历输出二维数组for(int j=0; j<a[i].length;j++){ //foriSystem.out.print(a[i][j]+" ");}System.out.println("");}}
}

Arrays类

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

数组的工具类:java.util.Arrays

copyOfRange(int[] original, int from, int to) // 第二个参数表示开始位置(取得到)// 第三个参数表示结束位置(取不到) 数组复制
toString() 转换为字符串
sort 排序
binarySearch 搜索
equals 判断是否相同
fill 填充
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] + " ");}}
}
package com.wang.array;import java.util.Arrays;public class Demo04 {public static void main(String[] args) {int[] a = {1,2,4,6,4,234,5423};System.out.println(a); //[I@1b6d3586,hashcode//打印数组元素Array.toStringSystem.out.println(Arrays.toString(a));int a[] = new int[] { 18, 62, 68, 82, 65, 9 };//复制数组 // copyOfRange(int[] original, int from, int to)// 第一个参数表示源数组// 第二个参数表示开始位置(取得到)// 第三个参数表示结束位置(取不到)int[] b = Arrays.copyOfRange(a, 0, 3);Arrays.sort(a); //排序System.out.println(Arrays.toString(a));Arrays.fill(a,2,4,0); //数组填充,二到四个元素被零填充[2,4)左闭右开System.out.println(Arrays.toString(a));Arrays.fill(a,0); //数组填充System.out.println(Arrays.toString(a));}
}

给数组赋值:通过Arrays.fill方法

对数组排序:通过Arrays.sort方法,按升序

比较数组:通过Arrays.equals方法比较数组中元素值是否相等

查找数组元素:通过Arrays.binarySearsh方法能对排序好的数组进行二分查找法操作

查询元素出现的位置
需要注意的是,使用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));
}
}

冒泡排序

总共有八大排序。

冒泡排序和选择排序

1.冒泡排序是比较相邻位置的两个数,而选择排序是按顺序比较,找最大值或者最小值;

2.冒泡排序每一轮比较后,位置不对都需要换位置,选择排序每一轮比较都只需要换一次位置;

3.冒泡排序是通过数去找位置,选择排序是给定位置去找数;

冒泡排序可以借助boolean变量结束外部循环,

嵌套循环,时间复杂度O(n2)

package com.wang.array;import java.util.Arrays;public class Demo05 {public static void main(String[] args) {/*冒泡排序1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置2.每一次比较,都会产生出一个最大,或者最小的数字3.下一轮则可以少一次排序4.依次循环,直到结束*/int[] a={1,2,3,4};int[] sort = sort(a);System.out.println(Arrays.toString(sort));}public static int[] sort(int[] array){ //优化-当有一轮没有交换位置的时候,说明已经排序好了,则结束int temp = 0;//外层循环,判断要走多少次for (int i = 0; i < array.length-1; i++) {boolean flag = false; //通过flag标识位减少没有意义的比较//内层循环,比价判断两个数,如果第一个数,比第二个数大,则交换位置for (int j = 0;j < array.length-1-i;j++){if (array[j+1]<array[j]){ //flag的作用点在这里!temp = array[j];array[j]= array[j+1];array[j+1]=temp;flag = true;}if (flag==false){break;}}return array;}
}

稀疏数组

稀疏数组的存放和读取

package com.wang.array;public class Demo06 {public static void main(String[] args) {int[][] a=new int[11][11];a[1][2]=1;a[2][3]=2;System.out.println("输出原始数组:");arraysPrint(a);System.out.println("===============");//转换为稀疏数组保存//获取有效值个数//1.计数不为零的个数int sum =0;for (int i = 0; i < a.length; i++) {for (int j = 0; j < a[i].length; j++) {if(a[i][j]!=0){sum++;}}}System.out.println("有效值的个数:"+sum);//2.创建一个稀疏数组int[][] a2=new int[sum+1][3];a2[0][0]=a.length;a2[0][1]=a[0].length;a2[0][2]=sum;//3.遍历二维数组,将非零值存放进去int count=0;//用来表示存放行数for (int i = 0; i < a.length; i++) {for (int j = 0; j < a[i].length; j++) {if(a[i][j]!=0){count++;a2[count][0]=i;a2[count][1]=j;a2[count][2]=a[i][j];}}}//4.输出稀疏数组System.out.println("稀疏数组:");arraysPrint(a2);System.out.println("===============");System.out.println("还原:");//1.读取稀疏数组值int[][] a3= new int[a2[0][0]][a2[0][1]];//2.给其中元素还原值for (int i = 1; i < a2.length; i++) {a3[a2[i][0]][a2[i][1]]=a2[i][2];}//输出还原数组arraysPrint(a3);}public static void arraysPrint(int[][] arrays){for (int[] ints : arrays) { // a.for遍历输出for (int anInt : ints) {System.out.print(anInt+"\t");}System.out.println();}}
}

arrays.for遍历输出

面向对象编程OOP

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

//创建一个对象
new Hero;
//使用一个引用来指向这个对象
Hero h = new Hero();
Hero h2 = h;  //h2指向h1所指向的对象

一个引用,同一时间,只能指向一个对象。

属性(这里的属性应该包含了构造器的定义)加方法变成一个类。

**面向对象思想:**抽象。物以类聚,分类的思维方式。对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。

三大特性:

  1. 封装
  2. 继承
  3. 多态

从认识论的角度:先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。

从代码运行的角度:先有类后有对象。类是对象的模板。

类与对象的创建

使用new关键字创建对象。

使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。

package com.wang.oop;//学生类
public class Student {//属性:字段String name;int age;//这里有一个默认的构造器// public Student() {// }//方法public void study(){System.out.println(this.name+"在学习");//this指这一个类}
}
package com.wang.oop;//一个项目应该只存在一个main方法
public class Application {public static void main(String[] args) {//类:抽象的,实例化//类实例化后会返回一个自己的对象!//student对象就是一个Student类的具体实例!Student xiaoming = new Student();Student xiaohong= new Student();xiaoming.name="小明";xiaoming.age= 3;System.out.println(xiaoming.name);System.out.println(xiaoming.age);}
}

构造器

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

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

类中的构造器也成为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:

  1. 必须和类的名字相同
  2. 必须没有返回类型,也不能写void

类的创建

package com.wang.oop;public class Person {//一个类即使什么都不写,它也会存在一个方法//显示的定义构造器String name;int age;//实例化初始值//使用new关键字,本质是在调用构造器//无参构造 默认构造器public Person(){ //Person person = new Person();想使用这样new Person()时必须有这样一个构造器}//有参构造:一旦定义了有参构造,无参必须显示定义public Person(String name){ Person person = new Person("wang");自动判断,调用有参构造,相当于方法的重载this.name =name;//前面的name代表类,后面的代表传进来的参数}public Person(String name, int age){this.name = name;this.age = age;}
}
/*构造器:1.和类名相同2.没有返回值作用:1.new 本质在调用构造方法2.初始化对象的值注意点:1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造Alt + Insertthis. =  //this后面的指这一个类*/

类的调用

package com.wang.oop;public class Application02 {public static void main(String[] args) {Person person = new Person(); //因为使用了这个,所以需要显示定义一个无参构造Person person1 = new Person("wang");Person person2=new Person("wang",23);System.out.println(person.name);System.out.println(person1.name);System.out.println(person2.name+person2.age);}
}

this

this这个关键字,相当于普通话里的“
this即代表当前对象,也就是调用方法的对象

//参数名和属性名一样
//在方法体中,只能访问到参数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代表的是属性namethis.name = name;
}

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

package com.wang.oop.demo01;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);}}

创建对象内存分析

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

栈://声明数组

  1. 存放基本变量类型(会包含这个基本类型的具体数值)
  2. 引用对象的变量(会存放这个引用在堆里面的具体地址)

堆://创建数组、赋值

  1. 存放new的对象和数组

  2. 可以被所有的线程共享,不会存放别的对象引用

方法区:

  1. 可以被所有的线程共享
  2. 包含了所有的class和static变量

理解Java的值传递

关于teemo为什么最终仍然指向“旧”提莫:
第31行调用revive方法,实参为teemo,teemo本身是一个引用,占用一个内存单元,其中存放“旧”提莫的地址值,而java为值传递,所以该调用只是将teemo的值,即“旧”提莫的地址值,将该值拷贝下来,复制给形参h,此时teemo显然仍指向“旧”提莫,h现在存放“旧”提莫的地址值,但是通过调用构造方法,新建了一个Hero对象,并将"新"提莫的地址值赋值给h。
结果:teemo指向“旧”提莫,h指向“新”提莫。
理解关键:java基于值传递而非引用传递。
public class Hero {String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public Hero(){
}public Hero(String name,float hp){this.name = name;this.hp = hp;
}//复活
public void revive(Hero h){h = new Hero("提莫",383);
}public static void main(String[] args) {Hero teemo =  new Hero("提莫",383);//受到400伤害,挂了teemo.hp = teemo.hp - 400;         teemo.revive(teemo); //teemo.hp = -17;因为形参h传递到了实参teemo的指向,即地址,然后又指向了新构造的对象        //问题: System.out.println(teemo.hp); 输出多少? 怎么理解?
}
}

小结

  1. 类与对象

    类是一个模板:抽象;对象是一个具体的实例

  2. 方法

    定义、调用!

  3. 对象的引用

    引用类型:基本类型(8)

    对象是通过引用来操作的:栈–>堆

  4. 属性:字段Field 成员变量

    默认初始化:

    数字:0 0.0;char:u0000;boolean:false;引用:null

    修饰符 属性类型 属性名 = 属性值!

  5. 对象的创建和使用

    必须使用new 关键字创造对象,构造器

    对象的属性 wang.name

    对象的方法 wang.sleep()

  6. 静态的属性 属性

    动态的行为 方法

封装

“高内聚,低耦合:”

高内聚,类的内部数据操作细节自己完成,不允许外部干涉;低耦合,仅暴露少量的方法给外部使用。

**封装(数据的隐藏):**通常应禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏。

属性私有,get/set

/*

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 提高系统的可维护性
    */
package com.wang.oop.demo05;public class Student {//private:私有//属性私有private String name;private int id;private char sex;private int age;//提供一些可以操作这个属性的方法//提供一些public的get、set方法//get获得这个数据public String getName(){return this.name;}//set给这个数据设置值public void setName(String name){this.name=name;}//alt +insert 自动生成get、setpublic int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) { //封装可以规避这种不合法的数据if(age>130 || age<0){ //不合法this.age = 0;}else {this.age = age;}}
}
/*
1. 提高程序的安全性,保护数据
2. 隐藏代码的实现细节
3. 统一接口
4. 提高系统的可维护性*/

继承

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。

extends:扩展。子类是父类的扩展。

Java中类只有单继承,没有多继承!

package com.wang.oop.demo06;
//Java中,所有的类都默认直接或者间接继承Object类
//基类、父类
public class Person /*extend Object*/ {private int money = 10_0000_0000;public void say(){System.out.println("say hi");}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}//ctrl + H 打开继承项
}package com.wang.oop.demo06;
//派生类、子类
public class Student extends Person{}package com.wang.oop.demo06;
public class Application {public static void main(String[] args) {Student student = new Student();student.say(); //可以调用Person类中的方法}
}

Super

public class Student extends Person{public Student() {super(); //隐藏代码,调用了父类的无参构造,且调用父类的构造器,必须要在子类构造器的第一行//当父类没有无参构造,子类无参构造需要显示的定义父类有参}private String name = "da";public void test(String name){System.out.println(name); //zhuangSystem.out.println(this.name); //daSystem.out.println(super.name); //wang}
}
/*
父类Hero提供了一个有参的构造方法:
public Hero(String name){this.name = name;
}
但是没有提供无参的构造方法
子类应该怎么处理?
*/
package charactor;
public class Hero {public String name;protected float hp;public Hero(String name){this.name = name;}
//    故意不提供无参的构造方法
//    public Hero(){//    }     public static void main(String[] args) {     }
}//子类添加有参构造方法
public ADHero(String name) {super(name);System.out.println("adHero的构造方法");}

重写

重写都是方法的重写,和属性无关。

重写:需要又继承关系,子类重写父类的方法!

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:范围可以扩大: public > protected > default > private
  4. 抛出的异常:范围 可以被缩小,但不能扩大; ClassNotFoundException --> Exception(大)

为什么要重写:父类的功能,子类不一定需要,或者不一定满足。

Alt + Insert ; override;

package com.wang.oop.demo07;
//重写都是方法的重写,和属性无关。
public class A extends B{//Override 重写@Override //注解:有功能的注释!public void test() {super.test();}
}

隐藏

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

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

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

Hero h =new ADHero();

h.battleWin(); //battleWin是一个类方法
h是父类类型的引用
但是指向一个子类对象
h.battleWin(); 会调用父类的方法?还是子类的方法?

**父类引用指向子类对象: **

**静态方法, 引用是啥类型就输出啥类的方法; **

非静态方法, 对象是啥类型就输出啥类型的方法;

  //当继承的方法为静态方法时,父类指向子类,JVM使用的是静态绑定//当继承的方法不是静态方法时,父类指向子类,JVM使用的是动态绑定//想要继承的方法为静态的方法时,想要调用子类的重写方法,直接子类名称.静态方法//当出现private,final,static,以及构造器的时候,JVM会调用静态绑定

多态

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

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

类的多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态
父类引用指向子类对象

要实现类的多态,需要如下条件

  1. 父类(接口)引用指向子类对象
  2. 调用的方法有 重写
/*
如果物品的种类特别多,那么就需要设计很多的方法
比如useArmor,useWeapon等等这个时候采用多态来解决这个问题
设计一个方法叫做useItem,其参数类型是Item
如果是使用血瓶,调用该方法
如果是使用魔瓶,还是调用该方法
无论英雄要使用什么样的物品,只需要一个方法即可
*/
package charactor;import property.Item;
import property.LifePotion;
import property.MagicPotion;public class Hero {public String name;protected float hp;public void useItem(Item i){i.effect();}public static void main(String[] args) {Hero garen =  new Hero();garen.name = "盖伦";LifePotion lp =new LifePotion();MagicPotion mp =new MagicPotion();garen.useItem(lp);garen.useItem(mp);     }}

动态编译:类型:可扩展性更强;

即同一方法可以根据发送对象的不同而采用多种不同的行为方式。

多态注意事项:

  1. 多态是方法的多态,属性没有多态
  2. 父类和子类,有联系 (类型转换异常:ClassCastException!)
  3. 存在条件:继承关系,方法需要重写,父类引用指向子类对象! Father f1 = new Son();

有些方法不能重写:

  1. static 方法,属于类 不属于实例
  2. final 常量
  3. private 方法
package com.wang.oop;
import com.wang.oop.demo08.Person;
import com.wang.oop.demo08.Student;
public class Application {    public static void main(String[] args) {        //一个对象的实际类型是确定的        //可以指向的引用类型不确定:父类的引用指向子类        //Student 能调用的方法都是自己的或者继承父类的!        Student s1 = new Student();        //Person 父类型,可以指向子类,但是不能调用子类独有的方法        Person s2 = new Student();        Object s3 = new Student(); //Object是所有子类的祖宗类        // 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大!        s1.eat();        ((Student)s2).eat(); //强制转换 子类重写了父类的方法,执行子类的方法   }
}

Object

Object类是所有类的父类

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

toString

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

        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){ //Hero类向上转型if(o instanceof Hero){Hero h = (Hero) o; //Object类向下转型return this.hp == h.hp; //this指的是当前对象,也就是调用equal方法的对象,所以this.hp指的是h1.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));}
}

hashCode

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

线程同步相关方法

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

getClass()

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

练习

重写Item的 toString(), finalize()和equals()方法
toString() 返回Item的name + price
finalize() 输出当前对象正在被回收
equals(Object o) 首先判断o是否是Item类型,然后比较两个Item的price是否相同

package com.wang.oop.demo11;public class Item {public String name;protected float price;public String toString(){return this.name +"价格"+this.price;}public void finalize(){System.out.println("当前对象正在被回收");}public boolean equals(Object o){if (o instanceof Item){Item h = (Item) o;return this.price==h.price;}return false;}public static void main(String[] args) {Item s1 = new Item();s1.name="宝剑";s1.price=800;Item s2 = new Item();s2.name="好盾牌";s2.price=800;System.out.println(s1.toString());System.out.println(s1.equals(s2));}
}

final

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

修饰类

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

修饰方法

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

修饰基本类型变量

final修饰基本类型变量,表示该变量只有一次赋值机会
修饰引用

final修饰引用
h引用被修饰成final,表示该引用只有1次指向对象的机会

常量

对象转型

子类转父类(向上转型),说的通

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

以下是对完整的代码的关键行分析
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,但是彼此没有互相继承关系
把魔法英雄当做物理英雄来用”,在语义上也是说不通的

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

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

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

[Java 向上转型和向下转型](

1、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转型。

如Father father = new Son();

2、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转型,要向下转型,必须先向上转型为了安全可以用instanceof判断。

如father就是一个指向子类对象的父类引用,把father赋给子类引用son 即Son son =(Son)father;

其中father前面的(Son)必须添加,进行强制转换。

3、upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类方法有效,向上转型只能引用父类对象的属性,要引用子类对象属性,则要写getter函数。

4、向上转型的作用,减少重复代码,父类为参数,调有时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。

**父类引用指向子类对象: **

静态方法, 引用是啥类型就输出啥类的方法;

非静态方法, 对象是啥类型就输出啥类型的方法;

package com.wang.oop.Demo09;
/*
向上转型后父类引用不能调用子类自己的方法,就是父类没有但是子类的方法,如果调用不能编译通过,比如子类的speak方法。
非要调用子类的属性呢?如果不向下转型就需要给需要的属性写getter方法。class Male extends Human {String name = "Male";public String getName(){return this.name;}
}
非要调用子类扩展的方法,比如speak方法,就只能向下转型了。
*/public class Human {public void sleep() {System.out.println("Human sleep..");}public static  void doSleep(Human h){h.sleep();}//此时传递的参数是父类对象,但是实际调用时传递子类对象,就是向上转型。public static void main(String[] args) {Human h = new Male();// 向上转型doSleep(new Male());//此处匿名子类对象,当然实际应用时应该是用上面的向上转型公式,然后将子类对象传递进来,这样以后好在向下转型,此处没有向下转型,所以直接用了匿名类对象。doSleep(new Female());}
}class Male extends Human {@Overridepublic void sleep() {System.out.println("Male sleep..");}
}class Female extends Human {@Overridepublic void sleep() {System.out.println("Female sleep..");}
}

instanceof 和类型转换

用来测试一个对象是否为一个类的实例,用法为:

boolean result = obj instanceof Class // obj必须是引用类型,不能是基本类型

当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。

注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。

package com.wang.oop;
import com.wang.oop.demo06.Teacher;
import com.wang.oop.demo08.Person;
import com.wang.oop.demo08.Student;
public class Application {    public static void main(String[] args) {        //Object > String        //Object > Person > Teacher        //Object > Person > Student        Object object = new Student();        System.out.println(object instanceof Student); //true        System.out.println(object instanceof Person); //true        System.out.println(object instanceof Object); //true        System.out.println(object instanceof Teacher); //false        System.out.println(object instanceof String); //false        System.out.println("=========================");        Person person = new Student();        System.out.println(person instanceof Student); //true        System.out.println(person instanceof Person); //true        System.out.println(person instanceof Object); //true        // System.out.println(person instanceof String); //编译报错            }
}

x instanceof y

编译能不能通过取决于x与y有没有父子关系;看x引用的左边

结果是true还是false看的是x的子类型是不是y的子类型;即看x引用的右边

访问修饰符

private修饰属性

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

注: 红色字体,表示不可行

package/friendly/default

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

protected 受保护的

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

public 公共的

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

总结

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

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

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

static关键字

代码块:在构造方法前

static静态代码块:最先执行,且只执行一次

类属性

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

package com.wang.oop.demo01;
/*
类属性: 又叫做静态属性
对象属性: 又叫实例属性,非静态属性
如果一个属性声明成类属性,那么所有的对象,都共享这么一个值
给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。
*/
public class Hero {public String name; //实例属性,对象属性,非静态属性protected float hp;static String copyright= "版权由Riot Games公司所有";;//类属性,静态属性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

  1. 类.类属性

Hero.copyright

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

类方法

类方法: 又叫做静态方法

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

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

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

  1. 类.类方法

Hero.battleWin();

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

Math.random()

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

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

​ public String getName(){

​ return name;

​ }

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

属性初始化

对象属性初始化

对象属性初始化有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);}}

属性初始化顺序

类属性(静态变量声明、静态初始化块(前后顺序))>对象属性(变量属性声明、初始化块(前后顺序))>构造方法

单例模式

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

单例模式三要素

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

饿汉式单例模式

/*
GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new 得到新的实例。
GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取12行定义的对象,而且每一次都是获取同一个对象。 从而达到单例的目的。
这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例
*/
package com.wang.oop.demo02;public class GaintDragon {//私有化构造方法使得该类无法在外部通过new 进行实例化private GaintDragon(){}//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个private static GaintDragon instance = new GaintDragon();//public static 方法,提供给调用者获取12行定义的对象public static GaintDragon getInstance(){ //返回一个对象return instance;}}package com.wang.oop.demo02;public class TestGiantDragon{public static void main(String[] args){//new实例化会报错//GaintDragon g = new GaintDragon();//只能通过getInstance得到private对象的地址GaintDragon g1 = GaintDragon.getInstance();GaintDragon g2 = GaintDragon.getInstance();GaintDragon g3 = GaintDragon.getInstance();//都是一个对象System.out.println(g1==g2); //tureSystem.out.println(g1==g3); //ture}
}

懒汉式单例模式

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

package charactor;public class GiantDragon {//私有化构造方法使得该类无法在外部通过new 进行实例化private GiantDragon(){       }//准备一个类属性,用于指向一个实例化对象,但是暂时指向nullprivate static GiantDragon instance;//public static 方法,返回实例对象public static GiantDragon getInstance(){//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象if(null==instance){instance = new GiantDragon();}//返回 instance指向的对象return instance;}
}

饿汉式与懒汉式

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

懒汉式,是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量(鉴于同学们学习的进度,暂时不对线程的章节做展开)。
使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。

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

枚举

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

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

public enum Season {SPRING,SUMMER,AUTUMN,WINTER //注:因为是常量,所以一般都是全大写
}

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

package com.wang.oop.demo03;public class Application {public static void main(String[] args) {Season season = Season.SPRING; //实例switch (season){case SPRING:System.out.println("春天");break;case SUMMER:System.out.println("夏天");break;}}
}

遍历枚举

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

public class HelloWorld {public static void main(String[] args) {for (Season s : Season.values()) { //Season.value()System.out.println(s);}}
}
package com.wang.oop.demo03;public class Application {public enum Char{ //在类里面定义TANK,WIZARD,ASSASSIN,ASSIST,WARRIOR}public static void main(String[] args) {for (Char type:Char.values()){switch(type){case TANK :System.out.println("坦克");break;case ASSIST:System.out.println("");}}}
}

abstract抽象类

作用:子类继承的时候必须重写这个方法。避免开发的时候忘记重写了

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

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

package charactor;public abstract class Hero {String name;float hp;float armor;int moveSpeed;public static void main(String[] args) {}// 抽象方法attack
// Hero的子类会被要求实现attack方法
public abstract void attack();}

抽象类可以没有抽象方法

一旦一个类被声明为抽象类,就不能够被直接实例化

abstract抽象方法,只有方法名字,没有方法的实现。

  1. 不能new这个抽象类,只能靠子类去实现它;约束!
  2. 抽象类中可以写普通的方法。
  3. 抽象方法必须在抽象类中。

接口

接口:只有规范!自己无法写方法~专业的约束!约束和实现分离:面向接口编程。

接口的本质是契约!

声明接口的关键字是interface

接口中定义的基本类型都是静态常量:public static final

接口中的所有定义其实都是抽象的public abstract

*接口都需要实现类:类的结尾Impl,implements

实现了接口的类,就需要重写接口的方法

多继承,利用接口实现

package com.wang.oop.Demo04;public interface AD {public void physicAttack();
}public class ADHero implements AD{@Overridepublic void physicAttack() {System.out.println("physic attack");}public static void main(String[] args) {ADHero ad = new ADHero();ad.physicAttack();}
}

抽象类和接口的区别

区别1:
子类只能继承一个抽象类,不能继承多个
子类可以实现多个接口
区别2:
抽象类可以定义
public,protected,package,private
静态和非静态属性
final和非final属性
但是接口中声明的属性,只能是
public
静态
final的
即便没有显式的声明
注: 抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

    //resistMagic即便没有显式的声明为 public static final//但依然默认为public static finalint resistMagic = 0;

内部类

非静态内部类

可以直接在一个类里面定义

语法: 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 + "超神!");elseSystem.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$1这个类。//换言之,也就不是将抽象类Hero实例化了,实际上实例化的是匿名类Hero$1,这才是匿名的真意。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

默认方法

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

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

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

作用:

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

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

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

//问: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?
//ADAPhero 实现AD AP 接口,必须重写该接口的方法,这样才不会报错

UML图

UML-Unified Module Language
统一建模语言,可以很方便的用于描述类的属性,方法,以及类和类之间的关系

类图

接口图

继承关系

带箭头的实线,表示 Spider,Cat, Fish都继承于Animal这个父类.

实现关系

表示 Fish实现了 Pet这个接口

继承与接口练习

/*
this后加 . 用来调用非构造方法和成员熟悉,this后不加 . 用来调用构造方法。this(“”)用来调用本类中的构造方法。
super(参数)用来调用父类构造方法。*/

题目:

\1. 创建Animal类,它是所有动物的抽象父类。
\2. 声明一个受保护的整数类型属性legs,它记录动物的腿的数目。
\3. 定义一个受保护的构造器,用来初始化legs属性。
\4. 声明抽象方法eat。
\5. 声明具体方法walk来打印动物是如何行走的(包括腿的数目)。

\1. Spider继承Animal类。
\2. 定义默认构造器,它调用父类构造器来指明所有蜘蛛都是8条腿。
\3. 实现eat方法

根据UML类创建pet(宠物)接口
\1. 提供getName() 返回该宠物的名字
\2. 提供setName(String name) 为该宠物命名
\3. 提供 play()方法

\1. 该类必须包含String属性来存宠物的名字。
\2. 定义一个构造器,它使用String参数指定猫的名字;该构造器必须调用超类构造器来指明所有的猫都是四条腿。
\3. 另定义一个无参的构造器。该构造器调用前一个构造器(用this关键字)并传递一个空字符串作为参数
\4. 实现Pet接口方法。
\5. 实现eat方法。

public abstract class Animal {String name;protected int legs;protected Animal(String name, int legs){this.name=name;this.legs=legs;}public abstract void eat();public void walk(Animal h){System.out.println(name+"用"+legs+"条腿走路");}public static void main(String[] args) {Animal h = new Animal("猪",4) {@Overridepublic void eat() {System.out.println(this.name+"拱白菜");}};h.eat();h.walk(h);}
}public class Spider extends Animal{public Spider(String name , int legs){super(name, 8);}@Overridepublic void eat() {System.out.println(this.name+"吃虫子");}public static void main(String[] args) {Spider spider = new Spider("蜘蛛",8);spider.eat();spider.walk(spider);}
}public interface Pet {String getName();void setName(String name);void play();
}public class Cat extends Animal implements Pet {public Cat(String name,int legs){super(name,4);}/*this后加 . 用来调用非构造方法和成员熟悉,this后不加 . 用来调用构造方法。this(“”)用来调用本类中的构造方法。super(参数)用来调用父类构造方法。*/public Cat(){ //无参构造调用该类中前一个构造器this(" ",0);}@Overridepublic void eat() {System.out.println(this.name+"吃鱼");}@Overridepublic String getName() {System.out.println(name);return name;}@Overridepublic void setName(String name) {this.name=name;}@Overridepublic void play() {System.out.println(name+"自己玩");}public static void main(String[] args) {Cat cat = new Cat("猫",4);cat.eat();cat.getName();cat.setName("大猫");cat.getName();cat.walk(cat);cat.play();}
}

数字与字符串

装箱拆箱

封装类

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

package digit;public class TestNumber {public static void main(String[] args) {int i = 5;//把一个基本类型的变量,转换为Integer对象Integer it = new Integer(i);//把一个Integer对象,转换为一个基本类型的intint 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);//封装类型转换成基本类型int i2 = it.intValue(); //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;}
}

Integer.MAX_VALUE

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

拆箱与装箱

package com.wang.oop.demo13;public class Demo01 {public static void main(String[] args) {int i = 5;Integer it = new Integer(i);Integer it2 = i; //装箱int i2 = it; //拆箱byte b = 127;Byte by = new Byte(b);Byte by2 = b; //装箱byte b2 =by; //拆箱Byte by3 = i; //Byte和int不能自动装箱Integer it3 = b; //Integer和byte不能自动装箱byte b3 = it; //byte和Integer不能自动拆箱int i3 = by; //int和Byte能自动拆箱}
}

字符串转换

数字转字符串

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

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

字符串转数字parse

调用Integer的静态方法parseInt //parse解析的意思

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

常用Math类

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

package digit;public class TestNumber {public static void main(String[] args) {float f1 = 5.4f;float f2 = 5.5f;//5.4四舍五入即5System.out.println(Math.round(f1));//5.5四舍五入即6System.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); //2.71828...}
}

利用Math方法算出自然常数,与判断质数

package numberAndString.mathMethod;public class Test {public static void main(String[] args) {System.out.println(Math.E);int n = Integer.MAX_VALUE;System.out.println(Math.pow((1 + 1d / n), n));//自然常数int num =1;for (int i = 0; i < 10000000; i++) {boolean prime = isPrime(i);if(prime) {num+=1;}}System.out.println(num);}public static boolean isPrime(int a) {boolean flag = true;if (a < 2) {// 素数不小于2return false;} else {for (int i = 2; i <= Math.sqrt(a); i++) {if (a % i == 0) {// 若能被整除,则说明不是素数,返回falseflag = false;break;// 跳出循环}}}return flag;}
}

格式化输出

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

%s 表示字符串
%d 表示数字
%n 表示换行

%d表示整数,%f表示小数

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

printf和format能够达到一模一样的效果,在printf中直接调用了format

format格式化方法

package com.wang.oop.demo13;import java.util.Locale;public class Demo03 {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,不够补0System.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);}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4mtd8kua-1633015831054)(C:\Users\15021\AppData\Roaming\Typora\typora-user-images\image-20210905224605712.png)]

字符

保存一个字符的时候用char,封装类Character

        char c1 = 'a';char c2 = '1';//字符1,而非数字1char c3 = '中';//汉字字符char c4 = 'ab'; //只能放一个字符char c1 = 'a';Character c = c1; //自动装箱c1 = c;//自动拆箱//常用方法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'); //转换为字符串//常见转义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");

通过Scanner从控制台读取字符串,然后把字符串转换为字符数组
参考的转换方式:

String str = “abc123”;

char[] cs = str.toCharArray();

package com.wang.oop.demo13;import java.util.Scanner;public class Demo05 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("输入字符串:");String str = scanner.nextLine();char[] cs = str.toCharArray();for (int i = 0; i < cs.length; i++) {if(Character.isUpperCase(cs[i]))System.out.print(cs[i]);if(Character.isDigit(cs[i]))System.out.print(cs[i]);}scanner.close();}
}

字符串

字符串是一个类,所以我们见到的字符串都是对象。

常见创建字符串手段:
\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;//  通过+加号进行字符串拼接}
}

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

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

String 的表现就像是一个常量

字符串格式化String.format

        //直接使用+进行字符串连接,编码感觉会比较繁琐,并且维护性差,易读性差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的字符串,即空字符串

随机字符串

创建一个长度是5的随机字符串,随机字符有可能是数字,大写字母或者小写字母

package com.wang.oop.demo13;import java.util.Random;public class Demo06 {public static void main(String[] args) {//0-9 48-57; A-Z 65-90; a-z 97-122String str = creatString(5);System.out.println(str);}public static String creatString(int length){String str = "";int num;char c;Random rd = new Random();for(int i =0; i<length; i++){num = rd.nextInt(75)+48;  // 随机数范围:[48,122)if((num>=58 && num<=64) || (num>=91 && num<=96)){i--;continue;}c = (char)num;str = str +c;}return str;}
}

字符串数组排序

    public static String[] sort(String[] str){String temp = "";for (int i = 0; i< str.length; i++){for (int j = 0; j < str.length-i-1; j++) {char[] ch0 = str[j].toCharArray();char[] ch1 = str[j+1].toCharArray();if(Character.toLowerCase(ch0[0]) > Character.toLowerCase(ch1[0])){temp = str[j];str[j] = str[j+1];str[j+1] = temp;}}}return str;}

穷举法破解密码

    public static String crack(int length,String str){String result ="";char[] ch0 = str.toCharArray();for (int i = 0; i < length; i++) {for (int j = 33; j < 126; j++) {if(ch0[i]==(char)j)result+=(char)j;}}return result;}

操纵字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LT4q3Zm2-1633015831055)(C:\Users\15021\AppData\Roaming\Typora\typora-user-images\image-20210906233610594.png)]

charAt(int index) 获取指定位置的字符String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";         char c = sentence.charAt(0);       System.out.println(c);toCharArray() 获取对应的字符数组String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号"; char[] cs = sentence.toCharArray(); //获取对应的字符数组         System.out.println(sentence.length() == cs.length);subString 截取子字符串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 根据分隔符进行分隔String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";        //根据,进行分割,得到3个子字符串String subSentences[] = sentence.split(",");for (String sub : subSentences) {System.out.println(sub);}trim 去掉首尾空格String sentence = "        盖伦,在进行了连续8次击杀后,获得了 超神 的称号      ";         System.out.println(sentence);//去掉首尾空格System.out.println(sentence.trim());toLowerCase 全部变成小写
toUpperCase 全部变成大写String sentence = "Garen";         //全部变成小写System.out.println(sentence.toLowerCase());//全部变成大写System.out.println(sentence.toUpperCase());indexOf lastIndexOf 判断字符或者子字符串出现的位置
contains 是否包含子字符串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 只替换第一个String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号"; String temp = sentence.replaceAll("击杀", "被击杀"); //替换所有的         temp = temp.replaceAll("超神", "超鬼");        System.out.println(temp);         temp = sentence.replaceFirst(",","");//只替换第一个       System.out.println(temp);

例子

    public static String upperFirst(String sentence){ //每个单词的首字母都转换为大写String sentence1 = "";String[] str = sentence.split(" ");for (int i = 0; i < str.length; i++) {char ch0 =Character.toUpperCase(str[i].charAt(0));String str1 = str[i].substring(1);str[i] = ch0 + str1;sentence1 += str[i]+" ";}return sentence1;}public static void specilFirst(String sentence,char p){ //统计这段绕口令有多少个以p开头的单词,不分大小写String[] str = sentence.split(" ");p = Character.toUpperCase(p);int num = 0;for (int i = 0; i < str.length; i++) {char ch0 = Character.toUpperCase(str[i].charAt(0));if(p==ch0)num++;}System.out.printf("有%d个以%c开头的单词",num,p);System.out.println();}public static String upperLower(String sentence){ //间隔大写小写模式String sentence1 = "";String[] str = sentence.split(" ");for (int i = 0; i < str.length; i++) {char[] ch0 = str[i].toCharArray();for (int j = 0; j < ch0.length; j++) {if(j%2==0)ch0[j]=Character.toUpperCase(ch0[j]);elsech0[j]=Character.toLowerCase(ch0[j]);}String word = new String(ch0); //通过字符数组创建一个字符串对象sentence1 += word +" ";}return sentence1;}public static String upperLast(String sentence){ //每个单词的最后一个字母变大写String sentence1 = "";String[] str = sentence.split(" ");for (int i = 0; i < str.length; i++) {char[] ch0 = str[i].toCharArray();ch0[ch0.length-1] = Character.toUpperCase(ch0[ch0.length-1]);str[i]=new String(ch0);sentence1 += str[i]+" ";}return sentence1;}public static String lastWordUpperFirst(String sentence,String word){ //最后一个想找的单词首字母大写int index = sentence.lastIndexOf(word);char[] ch0 = sentence.toCharArray();ch0[index]=Character.toUpperCase(ch0[index]);String str = new String(ch0); //通过字符数组创建一个字符串对象return str;}
}

比较字符串

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

是否是同一个对象-特例

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

使用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); //falseSystem.out.println(str1.equals(str2));//完全一样返回trueSystem.out.println(str1.equals(str3));//大小写不一样,返回falseSystem.out.println(str1.equalsIgnoreCase(str3));//忽略大小写的比较,返回true}}

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

        String str1 = "the light";String start = "the";String end = "Ight";System.out.println(str1.startsWith(start));//以...开始System.out.println(str1.endsWith(end));//以...结束

StringBuffer

StringBuffer是可变长的字符串

append delete insert reverse 追加 删除 插入 反转
length capacity 长度 容量
        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这个位置插入 thereSystem.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数量是不一样的

        String str1 = "the";StringBuffer sb = new StringBuffer(str1);System.out.println(sb.length()); //内容长度System.out.println(sb.capacity());//总空间

StringBuffer性能

String与StringBuffer的性能区别?

生成10位长度的随机字符串
然后,先使用String的+,连接10000个随机字符串,计算消耗的时间
然后,再使用StringBuffer连接10000个随机字符串,计算消耗的时间

时间System.currentTimeMillis

package character;public class TestString {public static void main(String[] args) {int total = 10000;String s = randomString(10);StringBuffer sb = new StringBuffer();String str1 = "";long start = System.currentTimeMillis();for (int i = 0; i <total; i++) {str1+=s;}long end = System.currentTimeMillis();System.out.printf("使用字符串连接+的方式,连接%d次,耗时%d毫秒%n",total,end-start);total *=100;start = System.currentTimeMillis();for (int i = 0; i <total; i++) {sb.append(s);}end = System.currentTimeMillis();System.out.printf("使用StringBuffer的方式,连接%d次,耗时%d毫秒%n",total,end-start);}private static String randomString(int length) {String pool = "";for (short i = '0'; i <= '9'; i++) {pool += (char) i;}for (short i = 'a'; i <= 'z'; i++) {pool += (char) i;}for (short i = 'A'; i <= 'Z'; i++) {pool += (char) i;}char cs[] = new char[length];for (int i = 0; i < cs.length; i++) {int index = (int) (Math.random() * pool.length());cs[i] = pool.charAt(index);}String result = new String(cs);return result;}}

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

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

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

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

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

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

日期

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。

创建日期对象

        // 当前时间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”这样的格式需要日期格式化

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

System.currentTimeMillis()

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

练习

借助随机数,创建一个从1995.1.1 00:00:00 到 1995.12.31 23:59:59 之间的随机日期

package com.wang.oop.datad;import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;public class Date1995 {public static void main(String[] args) throws ParseException { //SimpleDateFormat 日期格式化类SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String str1 = "1995-01-01 00:00:00";String str2 = "1995-12-31 23:59:59";Date date1 = sf.parse(str1); //转换为日期形式System.out.println(sf.format(date1));Date date2 = sf.parse(str2);System.out.println(sf.format(date2));long d1 = date1.getTime();long d2 = date2.getTime();long date = (long)(Math.random()*(d2-d1)+d1);System.out.println(sf.format(date));}
}

SimpleDateFormat 日期格式化类

日期转字符串

y 代表年;M 代表月;d 代表日;H 代表24进制的小时;h 代表12进制的小时;m 代表分钟;s 代表秒;S 代表毫秒

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

练习

package com.wang.oop.datad;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;//准备一个长度是9的日期数组
//使用1970年-2000年之间的随机日期初始化该数组
//按照这些日期的时间进行升序排序
//比如 1988-1-21 12:33:22 就会排在 1978-4-21 19:07:23 前面,因为它的时间更小,虽然日期更大public class DateFormat {public static void main(String[] args) throws ParseException {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date start = sdf.parse("1970-1-1 0:0:0");Date end = sdf.parse("2000-12-31 23:59:59");Date[] dates = new Date[9];long rand;for (int i = 0; i <dates.length ; i++) {rand = (long)(Math.random()*(end.getTime()-start.getTime())+start.getTime());dates[i] = new Date(rand);}System.out.println("得到的随机日期数组:");for (int i = 0; i < dates.length; i++) {System.out.print(sdf.format(dates[i])+"\t\t");if (i%3==2)System.out.println();}long temp;for (int i = 0; i < dates.length; i++) {boolean flag = false;for (int j = 0; j <dates.length-i-1 ; j++) {if (dates[j].getTime()>dates[j+1].getTime()){temp = dates[j].getTime();dates[j] = new Date(dates[j+1].getTime());dates[j+1] = new Date(temp);flag = true;}}if (flag==false)break;}System.out.println("排序后的随机日期数组:");for (int i = 0; i < dates.length; i++) {System.out.print(sdf.format(dates[i])+"\t\t");if (i%3==2)System.out.println();}}
}

Calendar

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

采用单例模式获取日历对象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 com.wang.oop.datad;
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;public class TextCalendar {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()));c.setTime(now);c.add(Calendar.MONTH,2);c.set(Calendar.DATE,-2);System.out.println("下个月的倒数第三天:\t" +format(c.getTime()));}private static String format(Date time) {return sdf.format(time);}
}

异常

异常:Exception

检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。在编译时不能被简单的忽略。

运行时异常:是可能被程序员避免的异常。可以在编译时被忽略。

错误:错误不是异常,是脱离程序员控制的问题。在代码中通常被忽略。

异常处理机制

抛出异常

捕获异常

异常处理五个关键字:try、catch、finally、throw、throws

package com.wang.exception;public class Demo01 {public static void main(String[] args) {int a = 0;int b = 0;//Ctrl + Alt + Ttry{ //try监控区域System.out.println(a/b);}catch (Error e){ //catch(想要捕获的异常类型!) 捕获异常System.out.println("Error");e.printStackTrace(); //打印错误的栈信息}catch (Exception e){System.out.println("Exception");}catch (Throwable t){System.out.println("Throwable");}finally { //处理善后工作System.out.println("finally");}}}

在开发中,如果去调用别人写的方法时,是否能知道别人写的方法是否会发生异常?这是很难判断的。针对这种情况,Java总允许在方法的后面使用throws关键字对外声明该方法有可能发生异常,这样调用者在调用方法时,就明确地知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。

如下面代码

public static void main(String[] args) {// TODO Auto-generated method stubint result = divide(4,2);System.out.println(result);
}public static int divide(int x,int y) throws Exception
{int result = x/y;return result;
}

这时候 编译器上会有错误提示 Unhandled exception type Exception
所以需要对调用divide()方法进行try…catch处理

public static void main(String[] args) {try {int    result = divide(4,2);System.out.println(result);} catch (Exception e) {e.printStackTrace();}}public static int divide(int x,int y) throws Exception
{int result = x/y;return result;
}
}
package com.wang.exception;public class Demo02 {public static void main(String[] args) {new Demo02().test(0,0);}//假设这方法中,处理不了这个异常,方法上抛出异常throwspublic void test(int a,int b) throws ArithmeticException{if(b==0){throw new ArithmeticException(); //主动抛出的异常,一般在方法中使用}}
}
  1. 编译时异常

    在Java 中,Exception类中除了RuntimeException 类及其子类外都是编译时异常。编译时异常的特点是Java编译器会对其进行检查,如果出现异常就必须对异常进行处理,否则程序无法编译通过。

处理方法

使用try… catch 语句对异常进行捕获

使用throws 关键字声明抛出异常,调用者对其进行处理

2.运行时异常

RuntimeException 类及其子类运行异常。运行时异常的特点是Java编译器不会对其进行检查。也就是说,当程序中出现这类异常时,即使没有使用try… catch 语句捕获使用throws关键字声明抛出。程序也能编译通过。运行时异常一般是程序中的逻辑错误引起的,在程序运行时无法修复。例如 数据取值越界。

3.自定义异常

JDK中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况。例如divide()方法中不允许被除数为负数。为类解决这个问题,在Java中允许用户自定义异常,但自定义的异常类必须继承自Exception或其子类。例子如下

package www.kangxg.jdbc;public class DivideDivideByMinusException  extends Exception {/*** */
private static final long serialVersionUID = 1L;public DivideDivideByMinusException(){super();
}public DivideDivideByMinusException(String message)
{super(message);
}
}
package www.kangxg.jdbc;public class Example {public static void main(String[] args) throws Exception {try {int    result = divide(4,-2);System.out.println(result);} catch (DivideDivideByMinusException e) {System.out.println(e.getMessage());}}public static int divide(int x,int y) throws DivideDivideByMinusException
{if(y<0){throw new DivideDivideByMinusException("被除数是负数");}int result = x/y;return result;
}}

new FileInputStream(f)异常

Java中通过 new FileInputStream(f) 试图打开某文件,就有可能抛出文件不存在异常FileNotFoundException
如果不处理该异常,就会有编译错误

        File f= new File("d:/LOL.exe");//试图打开文件LOL.exe,会抛出FileNotFoundException,如果不处理该异常,就会有编译错误new FileInputStream(f);

异常处理常见手段: try catch finally throws

1.将可能抛出FileNotFoundException 文件不存在异常的代码放在try里

2.如果文件存在,就会顺序往下执行,并且不执行catch块中的代码

  1. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中
  2. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常

FileNotFoundException是Exception的子类,使用Exception也可以catch住FileNotFoundException

多异常捕捉办法

有的时候一段代码会抛出多种异常,比如

new FileInputStream(f);Date d = sdf.parse("2016-06-03");

这段代码,会抛出 文件不存在异常 FileNotFoundException 和 解析异常ParseException
解决办法之一是分别进行catch

catch (FileNotFoundException e) {System.out.println("d:/LOL.exe不存在");e.printStackTrace();
} catch (ParseException e) {System.out.println("日期格式解析错误");e.printStackTrace();
}

另一个种办法是把多个异常,放在一个catch里统一捕捉

catch (FileNotFoundException | ParseException e) {

这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型

    public static void main(String[] args) {File f = new File("d:/LOL.exe");try {System.out.println("试图打开 d:/LOL.exe");new FileInputStream(f);System.out.println("成功打开");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date d = sdf.parse("2016-06-03");} catch (FileNotFoundException | ParseException e) {if (e instanceof FileNotFoundException)System.out.println("d:/LOL.exe不存在");if (e instanceof ParseException)System.out.println("日期格式解析错误");e.printStackTrace();}

finally

无论是否出现异常,finally中的代码都会被执行

        try{System.out.println("试图打开 d:/LOL.exe");new FileInputStream(f);System.out.println("成功打开");}catch(FileNotFoundException e){System.out.println("d:/LOL.exe不存在");e.printStackTrace();}finally{System.out.println("无论文件是否存在, 都会执行的代码");}

throws

考虑如下情况:
主方法调用method1
method1调用method2
method2中打开文件

method2中需要进行异常处理
但是method2不打算处理,而是把这个异常通过throws****抛出去
那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了

public class TestException {public static void main(String[] args) {method1();}private static void method1() {try {method2();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}private static void method2() throws FileNotFoundException {File f = new File("d:/LOL.exe");System.out.println("试图打开 d:/LOL.exe");new FileInputStream(f);System.out.println("成功打开");}
}

throw 和 throws

throws与throw这两个关键字接近,不过意义不一样,有如下区别:
\1. throws 出现在方法声明上,而throw通常都出现在方法体内。
\2. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某个异常对象。

异常分类

可查异常: CheckedException
可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过

运行时异常RuntimeException指: 不是必须进行try catch的异常
常见运行时异常:
除数不能为0异常:ArithmeticException
下标越界异常:ArrayIndexOutOfBoundsException
空指针异常:NullPointerException
在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误
Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。

错误Error,指的是系统级别的异常,通常是内存用光了
默认设置下,一般java程序启动的时候,最大可以使用16m的内存
如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError
与运行时异常一样,错误也是不要求强制捕捉的

运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。 运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

Throwable

Throwable是类,Exception和Error都继承了该类
所以在捕捉的时候,也可以使用Throwable进行捕捉
如图: 异常分ErrorException
Exception里又分运行时异常可查异常

自定义异常

一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException
创建一个类EnemyHeroIsDeadException,并继承Exception
提供两个构造方法

  1. 无参的构造方法
  2. 带参的构造方法,并调用父类的对应的构造方法
class EnemyHeroIsDeadException extends Exception{public EnemyHeroIsDeadException(){}public EnemyHeroIsDeadException(String msg){super(msg);}
}

在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常
\1. 创建一个EnemyHeroIsDeadException实例
\2. 通过throw 抛出该异常
\3. 当前方法通过 throws 抛出该异常

在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因

package charactor;public class Hero {public String name;protected float hp;public void attackHero(Hero h) throws EnemyHeroIsDeadException{if(h.hp == 0){throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" );}}public String toString(){return name;}class EnemyHeroIsDeadException extends Exception{public EnemyHeroIsDeadException(){}public EnemyHeroIsDeadException(String msg){super(msg);}}public static void main(String[] args) {Hero garen =  new Hero();garen.name = "盖伦";garen.hp = 616;Hero teemo =  new Hero();teemo.name = "提莫";teemo.hp = 0;try {garen.attackHero(teemo);} catch (EnemyHeroIsDeadException e) {// TODO Auto-generated catch blockSystem.out.println("异常的具体原因:"+e.getMessage());e.printStackTrace();}}
}

每一个throws的异常都需要catch住

接口IStringBuffer中声明的方法需要抛出异常

    public static class IndexIsNegativeException extends Exception{public IndexIsNegativeException(){}public IndexIsNegativeException(String msg){super(msg);}}public static class IndexOutOfRangeException extends Exception{public IndexOutOfRangeException(){}public IndexOutOfRangeException(String msg){super(msg);}}public static class NullPointerException extends Exception{public NullPointerException(){}public NullPointerException(String msg){super(msg);}}
//**每一个throws的异常都需要catch住**
// 接口IStringBuffer中声明的方法需要抛出异常public void delete(int start, int end) throws IndexIsNegativeException,IndexOutOfRangeException,NullPointerException {//边界条件判断if (start<0)throw new IndexIsNegativeException("start下标为负异常");if (start>length)throw new IndexOutOfRangeException("start下标超出范围异常");if (end<0)throw new IndexIsNegativeException("end下标为负异常");if (end>length)throw new IndexOutOfRangeException("end下标超出范围异常");if (start>=end)throw new IndexIsNegativeException("下标异常");System.arraycopy(value,end,value,start,length-end);length -= end-start;}try{sb.insert(1000, "let ");System.out.println(sb);}catch (IndexOutOfRangeException i){System.out.println(i.getMessage());i.printStackTrace();} catch (NullPointerException n) {System.out.println(n.getMessage());n.printStackTrace();} catch (IndexIsNegativeException i) {System.out.println(i.getMessage());i.printStackTrace();}}

练习

这是一个类图
Account类: 银行账号
属性: balance 余额
方法: getBalance() 获取余额
方法: deposit() 存钱
方法: withdraw() 取钱
OverdraftException: 透支异常,继承Exception
属性: deficit 透支额

package exception;public class OverDraftException extends Exception{private double deficit;public double getDeficit() {return deficit;}public OverDraftException(String msg, double deficit) {super(msg);this.deficit = deficit;}}package exception;public class Account {protected double balance;public Account(double balance) {this.balance = balance;}public double getBalance() {return balance;}public void deposit(double amt){this.balance+=amt;}public void withdraw(double amt) throws OverDraftException{if(this.balance<amt)throw new OverDraftException("余额不足", amt-this.balance);this.balance-=amt;}public static void main(String[] args) {//开户存了1000Account a = new Account(1000);//存钱1000a.deposit(1000);//查看余额System.out.println(a.getBalance());try {//取2001a.withdraw(2001);} catch (OverDraftException e) {System.err.println("透支金额:"+e.getDeficit());e.printStackTrace();}}}

类: CheckingAccount 支票账户,具备透支额度,继承Account
属性:overdraftProtection 透支额度

package exception;public class CheckingAccount extends Account {private double overdraftProtection;public CheckingAccount(double balance) {super(balance);}public CheckingAccount(double balance, double overdraftProtection) {super(balance);this.overdraftProtection = overdraftProtection;}public void withdraw(double amt) throws OverDraftException {if (amt > this.balance + overdraftProtection) {double deficit = amt - (this.balance + overdraftProtection);throw new OverDraftException("透支额度超标", deficit);}this.balance -= amt;}public static void main(String[] args) {//开户存了1000块,拥有500的透支额度CheckingAccount a = new CheckingAccount(1000, 500);//存了1000a.deposit(1000);//查询余额System.out.println(a.getBalance());try {a.withdraw(600);System.out.println(a.getBalance());a.withdraw(600);System.out.println(a.getBalance());a.withdraw(600);System.out.println(a.getBalance());a.withdraw(600);System.out.println(a.getBalance());a.withdraw(600);System.out.println(a.getBalance());} catch (OverDraftException e) {System.err.println("透支超额:"+e.getDeficit());e.printStackTrace();}}}

I/O

f1.getAbsolutePath(); // 绝对路径

f.exist(); //文件是否存在

f.isDirectory(); //是否是文件夹

f.isFile(); //是否是文件(非文件夹)

f.length(); //文件长度

f.lastModified(); //文件最后修改时间

f.setLastModified(0); //设置文件修改时间为1970.1.1 08:00:00

f.renameTo(f2); //文件重命名

f.list(); // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

File[]fs= f.listFiles(); // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

f.getParent(); // 以字符串形式返回获取所在文件夹

f.getParentFile(); // 以文件形式返回获取所在文件夹

f.mkdir(); // 创建文件夹,如果父文件夹skin不存在,创建就无效

f.mkdirs(); // 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

f.createNewFile(); // 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

f.getParentFile().mkdirs(); // 所以创建一个空文件之前,通常都会创建父目录

f.listRoots(); // 列出所有的盘符c: d: e: 等等

f.delete(); // 刪除文件

f.deleteOnExit(); // JVM结束的时候,刪除文件,常用于临时文件的删除

        import java.io.File;// 绝对路径File f1 = new File("d:/LOLFolder");System.out.println("f1的绝对路径:" + f1.getAbsolutePath());// 相对路径,相对于工作目录,如果在eclipse中,就是项目目录File f2 = new File("LOL.exe");System.out.println("f2的绝对路径:" + f2.getAbsolutePath());// 把f1作为父目录创建文件对象File f3 = new File(f1, "LOL.exe");System.out.println("f3的绝对路径:" + f3.getAbsolutePath());File f = new File("d:/LOLFolder/LOL.exe");System.out.println("当前文件是:" +f);//文件是否存在System.out.println("判断是否存在:"+f.exists());//是否是文件夹System.out.println("判断是否是文件夹:"+f.isDirectory());//是否是文件(非文件夹)System.out.println("判断是否是文件:"+f.isFile());//文件长度System.out.println("获取文件的长度:"+f.length());//文件最后修改时间long time = f.lastModified();Date d = new Date(time);System.out.println("获取文件的最后修改时间:"+d);//设置文件修改时间为1970.1.1 08:00:00f.setLastModified(0);//文件重命名File f2 =new File("d:/LOLFolder/DOTA.exe");f.renameTo(f2);System.out.println("把LOL.exe改名成了DOTA.exe");System.out.println("注意: 需要在D:\\LOLFolder确实存在一个LOL.exe,\r\n才可以看到对应的文件长度、修改时间等信息");

遍历文件夹及子文件夹

package com.wang.IO;import java.io.File;public class Demo01 {static long minSize=Long.MAX_VALUE;static long maxSize=0;static File minFile = null;static File maxFile = null;public static void main(String[] args) {File f1 =new File("D:/电影");traverse(f1);System.out.printf("最大的文件是%s,其大小是%,d字节%n",maxFile.getAbsoluteFile(),maxFile.length());System.out.printf("最小的文件是%s,其大小是%,d字节%n",minFile.getAbsoluteFile(),minFile.length());}public static void traverse(File f1){if (f1.isFile()){if (f1.length()>maxSize){maxSize = f1.length();maxFile = f1;}if(f1.length()!=0 && f1.length()<minSize){minSize = f1.length();minFile = f1;}}if(f1.isDirectory()){File[] fs = f1.listFiles();if(null!=fs)for (File f : fs) {traverse(f); //递归}}}
}

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流
InputStream字节输入流
OutputStream字节输出流
用于以字节的形式读取和写入数据

所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。

            File f = new File("d:/lol.txt");// 创建基于文件的输入流FileInputStream fis = new FileInputStream(f);// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取

        try {//准备文件lol.txt其中的内容是AB,对应的ASCII分别是65 66File f =new File("d:/lol.txt");//创建基于文件的输入流FileInputStream fis =new FileInputStream(f);//创建字节数组,其长度就是文件的长度byte[] all =new byte[(int) f.length()];//以字节流的形式读取文件所有内容fis.read(all);for (byte b : all) {//打印出来是65 66System.out.println(b);}//每次使用完流,都应该进行关闭fis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}

OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

注: 如果文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

    public static void main(String[] args) {try {// 准备文件lol2.txt其中的内容是空的File f = new File("d:/lol2.txt");// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Ybyte data[] = { 88, 89 };// 创建基于文件的输出流FileOutputStream fos = new FileOutputStream(f);// 把数据写入到输出流fos.write(data);// 关闭输出流fos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
    public static void main(String[] args) {try {File f = new File("d:/xyz/abc/def/lol2.txt");//因为默认情况下,文件系统中不存在 d:\xyz\abc\def,所以输出会失败//首先获取文件所在的目录File dir = f.getParentFile();//如果该目录不存在,则创建该目录if(!dir.exists()){//              dir.mkdir(); //使用mkdir会抛出异常,因为该目录的父目录也不存在dir.mkdirs(); //使用mkdirs则会把不存在的目录都创建好}byte data[] = { 88, 89 };FileOutputStream fos = new FileOutputStream(f);fos.write(data);fos.close();} catch (IOException e) {e.printStackTrace();}}

拆分文件与合并文件

public class TestStream {public static void main(String[] args) {//        int eachSize = 100*1024;
//        File srcFile = new File("D://JavaSE.md");
//        splitFile(srcFile,eachSize);murgeFile("d://新建文件夹","JavaSE.md");}private static void splitFile(File srcFile, int eachSize) {if (0 == srcFile.length())throw new RuntimeException("文件长度为0,不可拆分");byte[] fileContent = new byte[(int) srcFile.length()];// 先把文件读取到数组中try {FileInputStream fis = new FileInputStream(srcFile);fis.read(fileContent);fis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 计算需要被划分成多少份子文件int fileNumber;// 文件是否能被整除得到的子文件个数是不一样的// (假设文件长度是25,每份的大小是5,那么就应该是5个)// (假设文件长度是26,每份的大小是5,那么就应该是6个)if (0 == fileContent.length % eachSize)fileNumber = (int) (fileContent.length / eachSize);elsefileNumber = (int) (fileContent.length / eachSize) + 1;for (int i = 0; i < fileNumber; i++) {String eachFileName = srcFile.getName() + "-" + i;File eachFile = new File(srcFile.getParent(), eachFileName);byte[] eachContent;// 从源文件的内容里,复制部分数据到子文件// 除开最后一个文件,其他文件大小都是100k// 最后一个文件的大小是剩余的if (i != fileNumber - 1) // 不是最后一个eachContent = Arrays.copyOfRange(fileContent, eachSize * i, eachSize * (i + 1));else // 最后一个eachContent = Arrays.copyOfRange(fileContent, eachSize * i, fileContent.length);try {// 写出去FileOutputStream fos = new FileOutputStream(eachFile);fos.write(eachContent);// 记得关闭fos.close();System.out.printf("输出子文件%s,其大小是 %d字节%n", eachFile.getAbsoluteFile(), eachFile.length());} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/*** 合并的思路,就是从eclipse.exe-0开始,读取到一个文件,就开始写出到 eclipse.exe中,直到没有文件可以读* @param folder*            需要合并的文件所处于的目录* @param fileName*            需要合并的文件的名称* @throws FileNotFoundException*/private static void murgeFile(String folder, String fileName) {try {// 合并的目标文件File destFile = new File(folder, fileName);FileOutputStream fos = new FileOutputStream(destFile);int index = 0;while (true) {//子文件File eachFile = new File(folder, fileName + "-" + index++);//如果子文件不存在了就结束if (!eachFile.exists())break;//读取子文件的内容FileInputStream fis = new FileInputStream(eachFile);byte[] eachContent = new byte[(int) eachFile.length()];fis.read(eachContent);fis.close();//把子文件的内容写出去fos.write(eachContent);fos.flush();System.out.printf("把子文件 %s写出到目标文件中%n",eachFile);}fos.close();System.out.printf("最后目标文件的大小:%,d字节" , destFile.length());} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

关闭流

所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

在finally中关闭

这是标准的关闭流的方式
\1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
\2. 在finally关闭之前,要先判断该引用是否为空
\3. 关闭的时候,需要再一次进行try catch处理

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~

package stream;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;public class TestStream {public static void main(String[] args) {File f = new File("d:/lol.txt");FileInputStream fis = null;try {fis = new FileInputStream(f);byte[] all = new byte[(int) f.length()];fis.read(all);for (byte b : all) {System.out.println(b);}} catch (IOException e) {e.printStackTrace();} finally {// 在finally 里关闭流if (null != fis)try {fis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

使用try()的方式

把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

    public static void main(String[] args) {File f = new File("d:/lol.txt");//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭try (FileInputStream fis = new FileInputStream(f)) {byte[] all = new byte[(int) f.length()];fis.read(all);for (byte b : all) {System.out.println(b);}} catch (IOException e) {e.printStackTrace();}}

字符流

Reader字符输入流
Writer字符输出流
专门用于字符的形式读取和写入数据

FileReader 是Reader子类,以FileReader 为例进行文件读取

    public static void main(String[] args) {// 准备文件lol.txt其中的内容是ABFile f = new File("d:/lol.txt");// 创建基于文件的Readertry (FileReader fr = new FileReader(f)) {// 创建字符数组,其长度就是文件的长度char[] all = new char[(int) f.length()];// 以字符流的形式读取文件所有内容fr.read(all);for (char b : all) {// 打印出来是A BSystem.out.println(b);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

    public static void main(String[] args) {// 准备文件lol2.txtFile f = new File("d:/lol2.txt");// 创建基于文件的Writertry (FileWriter fr = new FileWriter(f)) {// 以字符流的形式把数据写入到文件中String data="abcdefg1234567890";char[] cs = data.toCharArray();fr.write(cs);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

文件加密与解密

public static void main(String[] args) {File f1 = new File("d://test.txt");File f2 = new File("d://test2.txt");File f3 = new File("d://test3.txt");encodeFile(f1,f2);decodeFile(f2,f3);
}public static void encodeFile(File encodingFile,File encodedFile){try(FileReader fr= new FileReader(encodingFile)){char[] all = new char[(int)encodingFile.length()];fr.read(all);for (int i = 0; i < all.length; i++) {if(all[i]=='9')all[i]='0';else if (all[i]<'9' && all[i]>='0')all[i]+=1;else if (all[i]=='z' | all[i]=='Z')all[i]-=25;else if ((all[i]<'z' && all[i]>='a')||(all[i]<'Z' && all[i]>='A'))all[i]+=1;}try(FileWriter fw = new FileWriter(encodedFile)){fw.write(all);}}catch(IOException e){e.printStackTrace();}
}public static void decodeFile(File decodingFile,File decodedFile){try(FileReader fr= new FileReader(decodingFile)){char[] all = new char[(int)decodingFile.length()];fr.read(all);for (int i = 0; i < all.length; i++) {if(all[i]=='0')all[i]='9';else if (all[i]<'9' && all[i]>='0')all[i]-=1;else if (all[i]=='a' | all[i]=='A')all[i]+=25;else if ((all[i]<'z' && all[i]>='a')||(all[i]<'Z' && all[i]>='A'))all[i]-=1;}try(FileWriter fw = new FileWriter(decodedFile)){fw.write(all);}}catch(IOException e){e.printStackTrace();}
}

编码方式

工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)

其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中

两个16进制一个字节,8个对应4个字节

比如在ISO-8859-1中,a 字符对应的数字是0x61
而UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

package stream;import java.io.UnsupportedEncodingException;public class TestStream {public static void main(String[] args) {String str = "中";showCode(str);}private static void showCode(String str) {String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };for (String encode : encodes) {showCode(str, encode);}}private static void showCode(String str, String encode) {try {System.out.printf("字符: \"%s\" 的在编码方式%s下的十六进制值是%n", str, encode);byte[] bs = str.getBytes(encode);for (byte b : bs) {int i = b&0xff;System.out.print(Integer.toHexString(i) + "\t");}System.out.println();System.out.println();} catch (UnsupportedEncodingException e) {System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\n", encode, str);}}
}

文件的编码方式-记事本

字符保存在文件中肯定也是以数字形式保存的,即对应在不同的棋盘上的不同的数字
记事本打开任意文本文件,并且另存为,就能够在编码这里看到一个下拉。
ANSI 这个不是ASCII的意思,而是采用本地编码**的意思。如果你是中文的操作系统,就会使GBK,如果是英文的就会是ISO-8859-1

Unicode UNICODE原生的编码方式
Unicode big endian 另一个 UNICODE编码方式
UTF-8 最常见的UTF-8编码方式,数字和字母用一个字节, 汉字用3个字节。

  byte[] bs = str.getBytes("UTF-8");String str = new String(bs,"GBK");int i = b&0xff;System.out.print(Integer.toHexString(i) + "\t");

FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符
而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

new InputStreamReader(new FileInputStream(f),Charset.forName(“UTF-8”));

        File f = new File("E:\\project\\j2se\\src\\test.txt");System.out.println("默认编码方式:"+Charset.defaultCharset());//FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了//而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBKtry (FileReader fr = new FileReader(f)) {char[] cs = new char[(int) f.length()];fr.read(cs);System.out.printf("FileReader会使用默认的编码方式%s,识别出来的字符是:%n",Charset.defaultCharset());System.out.println(new String(cs));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}//FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替//并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {char[] cs = new char[(int) f.length()];isr.read(cs);System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");System.out.println(new String(cs));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}

缓存流

以介质是硬盘为例,字节流和字符流的弊端
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作

缓存字符输入流 BufferedReader 可以一次读取一行数据

创建文件字符流
缓存流必须建立在一个存在的流的基础上

    public static void main(String[] args) {// 准备文件lol.txt其中的内容是// garen kill teemo// teemo revive after 1 minutes// teemo try to garen, but killed againFile f = new File("d:/lol.txt");// 创建文件字符流// 缓存流必须建立在一个存在的流的基础上try (FileReader fr = new FileReader(f);BufferedReader br = new BufferedReader(fr);){while (true) {// 一次读一行String line = br.readLine();if (null == line)break;System.out.println(line);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

PrintWriter 缓存字符输出流, 可以一次写出一行数据

缓存流必须建立在一个存在的流的基础上

    public static void main(String[] args) {// 向文件lol2.txt中写入三行语句File f = new File("d:/lol2.txt");try (// 创建文件字符流FileWriter fw = new FileWriter(f);// 缓存流必须建立在一个存在的流的基础上              PrintWriter pw = new PrintWriter(fw);              ) {pw.println("garen kill teemo");pw.println("teemo revive after 1 minutes");pw.println("teemo try to garen, but killed again");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush

        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {pw.println("garen kill teemo");//强制把缓存中的数据写入硬盘,无论缓存是否已满pw.flush();           pw.println("teemo revive after 1 minutes");pw.flush();pw.println("teemo try to garen, but killed again");pw.flush();}...

移除文件中的注释

    public static void removeComments(File javaFile){StringBuilder sb = new StringBuilder();//定义字符串容器//读取数据try(FileReader fr = new FileReader(javaFile);BufferedReader br = new BufferedReader(fr);){while(br.ready()){String str = br.readLine();     //读取数据if(str.trim().startsWith("//")){    //过滤单行注释continue;}sb.append(str + "\n");  //添加到字符串容器}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}//写入数据try(FileWriter fw = new FileWriter(javaFile);PrintWriter pw = new PrintWriter(fw);){pw.write(sb.toString());    //写入读取数据} catch (IOException e) {e.printStackTrace();}}

数据流

DataInputStream 数据输入流
DataOutputStream 数据输出流

使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

    public static void main(String[] args) {write();read();}private static void read() {File f =new File("d:/lol.txt");try (FileInputStream fis  = new FileInputStream(f);DataInputStream dis =new DataInputStream(fis);){boolean b= dis.readBoolean();int i = dis.readInt();String str = dis.readUTF();System.out.println("读取到布尔值:"+b);System.out.println("读取到整数:"+i);System.out.println("读取到字符串:"+str);} catch (IOException e) {e.printStackTrace();}}private static void write() {File f =new File("d:/lol.txt");try (FileOutputStream fos  = new FileOutputStream(f);DataOutputStream dos =new DataOutputStream(fos);){dos.writeBoolean(true);dos.writeInt(300);dos.writeUTF("123 this is gareen");} catch (IOException e) {e.printStackTrace();}}

对象流

对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘

对象的所有属性都打包发送

一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口

**注:**把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

package charactor;import java.io.Serializable;public class Hero implements Serializable {//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号private static final long serialVersionUID = 1L;public String name;public float hp;}package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;import charactor.Hero;public class TestStream {public static void main(String[] args) {//创建一个Hero garen//要把Hero对象直接保存在文件上,务必让Hero类实现Serializable接口Hero h = new Hero();h.name = "garen";h.hp = 616;//准备一个文件用于保存该对象File f =new File("d:/garen.lol");try(//创建对象输出流FileOutputStream fos = new FileOutputStream(f);ObjectOutputStream oos =new ObjectOutputStream(fos);//创建对象输入流              FileInputStream fis = new FileInputStream(f);ObjectInputStream ois =new ObjectInputStream(fis);) {oos.writeObject(h);Hero h2 = (Hero) ois.readObject();System.out.println(h2.name);System.out.println(h2.hp);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
package com.wang.IO;
import com.wang.IO.Hero;import java.io.*;
/*
准备一个长度是10,类型是Hero的数组,使用10个Hero对象初始化该数组然后把该数组序列化到一个文件heros.lol接着使用ObjectInputStream 读取该文件,并转换为Hero数组,验证该数组中的内容,是否和序列化之前一样*/
public class ObjectStream {public static void main(String[] args) {Hero[] hs = new Hero[10];for (int i = 0; i < hs.length; i++) {hs[i] = new Hero(new String("hero"+i),600);}File f = new File("D:/test.txt");try(FileOutputStream fos = new FileOutputStream(f);ObjectOutputStream oos = new ObjectOutputStream(fos);FileInputStream fis = new FileInputStream(f);ObjectInputStream ois = new ObjectInputStream(fis);){oos.writeObject(hs);Hero[] h2 = (Hero[])ois.readObject();for (Hero hero : h2) {System.out.println(hero.name);System.out.println(hero.hp);System.out.println(hero.money);}}catch (IOException e){e.printStackTrace();}catch(ClassNotFoundException e){e.printStackTrace();}}
}

System.in

System.out 是常用的在控制台输出数据的
System.in 可以从控制台输入数据

package stream;import java.io.IOException;
import java.io.InputStream;public class TestStream {public static void main(String[] args) {// 控制台输入try (InputStream is = System.in;) {while (true) {// 敲入a,然后敲回车可以看到// 97 13 10// 97是a的ASCII码// 13 10分别对应回车换行int i = is.read();System.out.println(i);}} catch (IOException e) {e.printStackTrace();}}

自动创建有一个属性的类文件。
通过控制台,获取类名,属性名称,属性类型,根据一个模板文件,自动创建这个类文件,并且为属性提供setter和getter

package stream;import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;public class TestStream {public static void main(String[] args) {// 接受客户输入Scanner s = new Scanner(System.in);System.out.println("请输入类的名称:");String className = s.nextLine();System.out.println("请输入属性的类型:");String type = s.nextLine();System.out.println("请输入属性的名称:");String property = s.nextLine();String Uproperty = toUpperFirstLetter(property);// 读取模版文件File modelFile = new File("E:\\project\\j2se\\src\\Model.txt");String modelContent = null;try (FileReader fr = new FileReader(modelFile)) {char cs[] = new char[(int) modelFile.length()];fr.read(cs);modelContent = new String(cs);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}      //替换String fileContent = modelContent.replaceAll("@class@", className);fileContent = fileContent.replaceAll("@type@", type);fileContent = fileContent.replaceAll("@property@", property);fileContent = fileContent.replaceAll("@Uproperty@", Uproperty);String fileName = className+".java";//替换后的内容System.out.println("替换后的内容:");System.out.println(fileContent);File file = new File("E:\\project\\j2se\\src",fileName);try(FileWriter fw =new FileWriter(file);){fw.write(fileContent);} catch (IOException e) {e.printStackTrace();}System.out.println("文件保存在:" + file.getAbsolutePath());}public static String toUpperFirstLetter(String str){char upperCaseFirst =Character.toUpperCase(str.charAt(0));String rest = str.substring(1);return upperCaseFirst + rest;}
}

ArrayList

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是
ArrayList
容器的容量"capacity"会随着对象的增加,自动增长
只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9O031LZe-1633015948494)(C:\Users\15021\AppData\Roaming\Typora\typora-user-images\image-20210922224739825.png)]

add 有两种用法
第一种是直接add对象,把对象加在最后面

heros.add(new Hero("hero " + i));

第二种是在指定位置加对象

heros.add(3, specialHero);

    public static void main(String[] args) {ArrayList heros = new ArrayList();// 把5个对象加入到ArrayList中for (int i = 0; i < 5; i++) {heros.add(new Hero("hero " + i));}System.out.println(heros);// 在指定位置增加对象Hero specialHero = new Hero("special hero");heros.add(3, specialHero);System.out.println(heros.toString());}

通过方法contains 判断一个对象是否在容器中
判断标准: 是否是同一个对象,而不是name是否相同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRJUYfc2-1633015948495)(C:\Users\15021\AppData\Roaming\Typora\typora-user-images\image-20210922225134583.png)]

通过get获取指定位置的对象,如果输入的下标越界,一样会报错

indexOf用于判断一个对象在ArrayList中所处的位置
与contains一样,判断标准是对象是否相同,而非对象的name值是否相等

remove用于把对象从ArrayList中删除
remove可以根据下标删除ArrayList的元素

heros.remove(2);

也可以根据对象删除

heros.remove(specialHero);

set用于替换指定位置的元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-guyWB3jG-1633015948496)(C:\Users\15021\AppData\Roaming\Typora\typora-user-images\image-20210922225432468.png)]

size 用于获取ArrayList的大小

heros.size();

toArray可以把一个ArrayList对象转换为数组。
需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

package collection;import java.util.ArrayList;import charactor.Hero;public class TestCollection {public static void main(String[] args) {ArrayList heros = new ArrayList();// 初始化5个对象for (int i = 0; i < 5; i++) {heros.add(new Hero("hero " + i));}Hero specialHero = new Hero("special hero");heros.add(specialHero);System.out.println(heros);Hero hs[] = (Hero[])heros.toArray(new Hero[]{});System.out.println("数组:" +hs);}
}

addAll 把另一个容器所有对象都加进来

heros.addAll(anotherHeros);

clear 清空一个ArrayList

heros.clear();

List接口

ArrayList实现了接口List
常见的写法会把引用声明为接口List类型
注意:是java.util.List,而不是java.awt.List

    public static void main(String[] args) {//ArrayList实现了接口List//常见的写法会把引用声明为接口List类型//注意:是java.util.List,而不是java.awt.List//接口引用指向子类对象(多态)List heros = new ArrayList();heros.add( new Hero("盖伦"));System.out.println(heros.size());}

泛型Generic

不指定泛型的容器,可以存放任何类型的元素
指定了泛型的容器,只能存放指定类型的元素以及其子类

    public static void main(String[] args) {//对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品List heros = new ArrayList();heros.add(new Hero("盖伦"));//本来用于存放英雄的容器,现在也可以存放物品了heros.add(new Item("冰杖"));//对象转型会出现问题Hero h1=  (Hero) heros.get(0);//尤其是在容器里放的对象太多的时候,就记不清楚哪个位置放的是哪种类型的对象了Hero h2=  (Hero) heros.get(1);//引入泛型Generic//声明容器的时候,就指定了这种容器,只能放Hero,放其他的就会出错List<Hero> genericheros = new ArrayList<Hero>();genericheros.add(new Hero("盖伦"));//如果不是Hero类型,根本就放不进去//genericheros.add(new Item("冰杖"));//除此之外,还能存放Hero的子类genericheros.add(new APHero());//并且在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类Hero h = genericheros.get(0);}

为了不使编译器出现警告,需要前后都使用泛型,像这样:

List genericheros = new ArrayList();
不过JDK7提供了一个可以略微减少代码量的泛型简写方式

List genericheros2 = new ArrayList<>();

后面的泛型可以用<>来代替,聊胜于无吧

遍历

for循环遍历

        // 第一种遍历 for循环System.out.println("--------for 循环-------");for (int i = 0; i < heros.size(); i++) {Hero h = heros.get(i);System.out.println(h);}

使用迭代器Iterator遍历集合中的元素

for (Iterator iterator = heros.iterator(); iterator.hasNext()

how2j学习总结-未完相关推荐

  1. pythonb超分辨成像_Papers | 超分辨 + 深度学习(未完待续)

    1. SRCNN 1.1. Contribution end-to-end深度学习应用在超分辨领域的开山之作(非 end-to-end 见 Story.3 ). 指出了超分辨方向上传统方法( spar ...

  2. Linux学习总结 (未完待续...)

    Linux学习总结: 1.用户管理部分 a,用户与组配置文件 a1.与用户和组相关的配置文件:passwd,shadow group,gshadow a2.超级权限控制Sudo的配置文件:/etc/s ...

  3. c++课程学习(未完待续)

    关于c++课程学习 按照计划,我首先阅读谭浩强c++程序设计一书的ppt,发现第一章基本上都是很基础的东西. 同时,书中与班导师一样,推荐了使用visual c++. 而师爷的教程里面推荐使用的是ec ...

  4. Multimodal Deep Learning(多模态深度学习)未完待续

    摘要: 本文提出一种在深度网络上的新应用,用深度网络学习多模态.特别的是,我们证明了跨模态特征学习--如果在特征学习过程中多模态出现了,对于一个模态而言,更好的特征可以被学习(多模态上学习,单模态上测 ...

  5. 个体软件过程(PSP)学习笔记 (未完)

    个体软件过程 前言 软件工程漫谈 软件工程认识观 标准定义 将系统化的.规范的.可度量的方法应用于软件的开发.运行和维护的过程,即将工程化应用于软件中;以上所述方法的研究 软件开发管理 项目管理是基础 ...

  6. linux安装java学习环境(未完待续)

    linux安装java学习环境 数据库连接命令 连接mysql数据库 格式为: mysql -hip地址 -p端口号 -u root -p 输入密码例如: mysql -hlocalhost -p33 ...

  7. 【iOS开发】—— SDWebImage源码学习(未完)

    文章目录 什么是SDWebImage? sd_setImageWithURL调用关系 步骤一 步骤三 步骤四 步骤五 步骤六 下载步骤 UIImageView+ WebCache UIView+ We ...

  8. nmap学习记录(未完待续)

    这里写自定义目录标题 声明! 学习内容 一.端口查询 二.服务指纹 三.局域网探测 reference 声明! 请勿从事违法行为! 学习内容 一.端口查询 原理 常规扫描 nmap scanme.nm ...

  9. XSS学习笔记(未完)

    XSS基础学习 1. XSS 1.1 客户端Cookie: 1.2 XSS攻击类型 1.3 工具/平台 1.4 利用方式 1.4.1 非手工方式 1.4.1.1自动化攻击:beef 1.4.2 手工方 ...

最新文章

  1. 程序员奶爸用树莓派制作婴儿监护仪:哭声自动通知,还能分析何时喂奶
  2. UVA 11491 Erasing and Winning 奖品的价值 (贪心)
  3. python flask 配置处理
  4. 高德地图哪个语音包最好_高德地图妲己语音
  5. Python中super()和__init__()方法
  6. 【Java类加载机制】深入加载器
  7. 线程的局部变量ThreadLocal概念
  8. FreeBSD 10 将使用 Clang 编译器替换 GCC
  9. Twitter Storm安装配置(Ubuntu系统)单机版
  10. JDK源码(15)-Class
  11. Silverlight 中文教程第四部分:使用 Style 元素更好地封装观感 (木野狐译)
  12. python调用hive与java调用区别_使用Pyhive调用
  13. linux 命令是什么的缩写,Linux一部分命令解释(命令缩写代表什么意思)
  14. 百度搜索查找关键词技巧-信息收集能力
  15. (伪)原创,采集工具应用
  16. 2.1 A k-armed Bandit Problem
  17. ECM 手机MIC电路简单设计描述
  18. 汇编语言课程设计动态图形设计小车动态图形
  19. 论文投稿变量书写格式汇总
  20. 操作系统真象还原第5章:保护模式进阶,向内核进阶

热门文章

  1. app测试、web测试-怎么测?
  2. 用html做一个旅游网首页
  3. Python数据可视化第 2 讲:matplotlib 绘图中文字体设置
  4. 100+文档格式预览,私有化部署,10分钟搭建专属IM
  5. 网络基础知识 TCP UDP IP
  6. Android利用Java反射获取用户手机的rom定制系统及版本,EMUI,MIUI,ColorOS,FunthouchOS等
  7. FIX协议介绍与QuickFIX使用入门(上)
  8. [POI2014]DOO-Around the world
  9. 轻微课学画画好不好:我的体验反馈
  10. 基础的http协议构成