1.前期准备
1. 快捷键
-
ctrl+a 全选
ctrl+x 剪切
ctrl+c 复制
ctrl+v 粘贴
ctrl+z 撤销
ctrl+s 保存
alt +f4 关闭
shift+delete 永久删除文件 -
ctrl +d 选中一行,在下一行直接复制
-
win +r 命令行窗口
win+e 我的电脑esc+shift+ctrl 打开任务管理器 -
alt +tab 转换窗口
-
快速输出:for循环:fori
-
数组的增强for循环:数组名.for
-
代码块包裹,处理异常很快 ctrl +alt+t
- 重写to string:alt+insert
2.Dos命令
1.打开cmd
- win+R
- 在文件夹下,按住shift+鼠标右键
- 在资源管理器的地址栏上输入cmd
2.Dos命令
-
盘符切换 D: E :
-
查看当前目录下的所有文件 dir
-
切换目录:cd/d+盘符名+:
访问不同盘的文件:cd/d+盘符名+:+\+文件名 貌似访问不了C盘的文件 访问同盘的目录:cd+文件名 返回目录的上一级:cd..
-
清理屏幕 cls(clear screen)
-
退出终端:exit
-
查看电脑ip:ipconfig
-
打开计算器:calc
打开画图工具:mspaint
打开记事本:notepad
-
ping命令 得到网站的信息: ping www.baidu.com
-
文件操作
-
创建文件夹:ma+文件名
-
创建文件:cd>a.txt
-
删除文件:del a.txt
-
删除文件夹:rd +文件名
-
3.Markdown 学习
-
设置几级标题:Ctrl +数字
-
字体加粗,引用,图片,超链接 ,表格都是右键
-
有序列表, 数字+点+空格
-
无序列表 减号+空格
这两个都可以用右键来实现
2.Java基础
1.注释
1.单行注释://
// 单行注释
2.多行注释:/* *
/*
多行注释
多行注释
多行注释
*/
3.文档注释
//文档注释
/**
* @deprecated 描述
* @Author 作者
*/
2.关键字
3.标识符
4.数据类型扩展
int a1=10; // 十进制
int a2 =010; // 8进制
int a3=0x10; // 16进制
System.out.println(a1);
System.out.println(a2 );
System.out.println(a3 );
System.out.println("_____________________");
float f =0.1f;
double d =0.1;
System.out.println(f==d); //精度不同,所以结果不相等
float f1=569874569f;
float f2 =f1+1;
System.out.println(f1==f2);// 舍弃误差,所以不相等
// 所以,不要用两个不同的浮点数类型比较!!
System.out.println("_____________________");
char c ='来';
System.out.println((int)c); //强制转换
System.out.println("_____________________");
System.out.println("kk\tll"); // 转义字符 制表符
System.out.println("kk\nll"); // 转义字符 换行
10
8
16
_____________________
false
true
_____________________
26469
_____________________
kk ll
kk
ll
5.类型转换
/* 强制转换 低->高
自动转换 高->低
布尔值不能转换
转换可能会导致精度丢失
*/
double a5= 23.45;
System.out.println((int)a5);
int a6=4;
double a7=a6+1;
System.out.println(a7);
23
5.0
int a=100000000;
int b=200;
int i =a*b;
System.out.println(i); //内存溢出,超过范围了
long l =a*b;
System.out.println(l);// a和b 都是int 计算完后才传给Long
long l1 =(long)a*b; //要强制转换
System.out.println(l1);
-1474836480
-1474836480
20000000000
长路与 和 短路与
无论长路与还是短路与
两边的运算单元都是布尔值
都为真时,才为真
任意为假,就为假
区别
长路与 两侧,都会被运算
短路与 只要第一个是false,第二个就不进行运算了
6.变量
public class Demo01 {
/*
一是没有static修饰的,这些成员变量是对象中的成员,称为实例变量。
二是有static修饰的,称为类变量(静态变量)。
静态变量(类变量)具备以下特点:
1.随着类的加载而加载
2.优先于对象存在
3.被所有对象所共享
4.可以直接被类名调用
类变量和实例变量的区别是:
1,存放位置
类变量随着类的加载而存在于方法区中。
实例变量随着对象的建立而存在于堆内存中。
2,生命周期
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失。*/
int age; //实例变量,会默认有初始值0,0.0 false,
String b;
static int c =100; // 类变量
static final int v =100; //常量,定义后不能被改变。
public static void main(String[] args) {
int a2=10; /*局部变量,必须声明和初始化,
作用域只在大括号内*/
System.out.println(a2);
Demo01 demo01 = new Demo01();
System.out.println("+++++++++++");
System.out.println(demo01.age);//实例变量要通过对象来访问
System.out.println(demo01.b);
System.out.println("+++++++++++");
System.out.println(c); //可以直接访问
System.out.println(Demo01.c); //或者类名
}
public void add()
{
int c; //局部变量
}
}
10
+++++++++++
0
null
+++++++++++
100
100
7.运算符
int a =10;
int b =10;
System.out.println(a+b+""); //计算前面的,后面类型以前面为准
System.out.println(""+a+b);
}
20
1010
8.包机制
9.Javadoc
/**
* @author=AG
* @param a
* @throws Exception
*/
// javadoc 放在方法前,自动生产
public void add(int a) throws Exception{
}
用idea 生成 APi
3.Java 流程控制
1.Scanner
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String s =scanner.next();
System.out.println(s);
10
1131115111
请输入:
你骄傲啊
你骄傲啊
Scanner scanner = new Scanner(System.in);
int a =scanner.nextInt(); //只可以输入整数
double d =scanner.nextDouble(); //只可以输入小数或者整数
float f =scanner.nextFloat();//只可以输入小数或者整数
String a1=scanner.nextLine();//遇见回车停止
String a2=scanner.next();//遇见回车和空格停止
System.out.println(a2);
2.if选择语句
Scanner scanner =new Scanner(System.in);
int a = scanner.nextInt();
while (a>=0){
if(a>=80){
System.out.println("优");
}
else if (60<a&&a<80){
System.out.println("良");
}
else{
System.out.println("差");
}
a = scanner.nextInt();
}
3.switch选择语句
Scanner scanner=new Scanner(System.in);
char c= (char)System.in.read();
switch (c){
case 'a':
System.out.println("a");
break;
case 'b':
System.out.println("b");
default:
System.out.println("c");
}
b
b
c
——————————————————————————
a
a
4.while 循环语句
int sum=0;
int i= 100;
while (i>=0){
sum+=i;
i--;
}
System.out.println(sum);
sum =0;
do{
i++;
sum+=i;
}while (i<100);
System.out.println(sum);
5.for循环语句
for(int i=0;i<10;i++){
System.out.print("第"+i+"天");
System.out.println(Math.pow(2,i));
}
System.out.println("----------------");
//增强for
int [] a={1,23,56,8};
for(int i:a){
System.out.println(i);
}
第0天1.0
第1天2.0
第2天4.0
第3天8.0
第4天16.0
第5天32.0
第6天64.0
第7天128.0
第8天256.0
第9天512.0
----------------
1
23
56
8
6.break和continue
//break
for (int i=0;i<5;i++){
System.out.println("i"+i);
for (int j=0;j<5;j++){
break;
}
}
i0
i1
i2
i3
i4
//continue
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
if (i < 3) continue;
System.out.println(i+"i和j"+j );
}
}
3i和j0
3i和j1
3i和j2
4i和j0
4i和j1
4i和j2
4.Java方法
1.方法的定义
2.值传递和引用传递
public static void main(String[] args) {
int a=0;
add(a);
System.out.println("主函数" + a);
}
//值传递
public static void add(int a) {
a += 10;
System.out.println("方法" + a);
}
方法10
主函数0
package day2;
public class Demo08 {
int a=10;
public static void main(String[] args) {
Demo08 demo08 =new Demo08();
int a=10;
Add(demo08);
System.out.println("主函数"+ demo08.a);
}
// 引用传递
public static void Add(Demo08 d){
d.a+=10;
System.out.println("方法"+d.a);
}
}
方法20
主函数20
3.方法的重载
方法名必须相同,
参数列表必须不同(个数不同,类型不同,参数排列不同)
public void Add (int a){
}
public void Add (int b){ //换形参名字不行
}
public void Add (double a){
}
public void Add (double a,int b){
}
public void Add (int a,double b){ //两个形参调换顺序也行
}
public int Add (int a,double b){ //改变返回值类型也不行
return 0;
}
4.命令行参数
有时候你希望运行一个程序时候再传递给它消息。这要靠传递命令行参数给main()函数实现。
命令行参数是在执行程序时候紧跟在程序名字后面的信息。
package day2;
public class Demo10 {
public static void main(String[] args) {
for(int i=0; i<args.length; i++){
System.out.println("args[" + i + "]: " + args[i]);
}
}
}
5.可变参数
不知道形参有多少个
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
package day2;
public class Demo11 {
public static void main(String[] args) {
add(1);
add(1,2,3,5);
add(1,5,8,90,65);
}
public static void add (double a,int...i){
System.out.println(i.length);
}
}
0
3
4
6.递归
自身调用自身
package day2;
public class Demo12 {
public static void main(String[] args) {
System.out.println(fun(100));
}
public static int fun(int n){
if (n==1) { //递归的终止条件
return 1;
}else {
return n+fun(n-1);
}
}
}
5050
5.Java数组
1.数组的创建和初始化
int [] a1=new int[5]; //方法1
int [] a2=new int[]{1,3,4}; //方法2 分配空间时同时赋值 空间固定为3
//int [] a3=new int[3]{1,2,3}; //不能分配空间同时指定数组内容
int [] a4={1,2,3}; //方法3 不用new 对象
int [] a5; //方法4,先定义,后传递对象
a5=new int[3];
2.数组的使用
package day2;
public class Demo14 {
public static void main(String[] args) {
int [] a=new int[]{1,2,3,4,5};
print(a);
System.out.println("_______");
//增强for循环 快捷键:数组名.for
for (int i : a) {
System.out.println(i);
}
System.out.println("_______");
for (int i : reverse(a)) { //输出反转的数组
System.out.println(i);
}
}
public static void print (int [] a){ //数组可以当做参数传入
a[0]=10; /* 数组传递是引用传递,相当于把地址传入,所以当方法内改变数组后,
main方法也会改变*/
for (int i : a) {
System.out.print(i);
}
}
//数组反转
public static int [] reverse(int [] a){ //数组作为返回值返回
int [] r =new int[a.length];
int n=0;
for (int i : a) {
r[a.length-1-n]=i;
n++;
}
return r;
}
}
102345_______
10
2
3
4
5
_______
5
4
3
2
10
Process finished with exit code 0
3.二维数组
package day2;
public class Demo15 {
public static void main(String[] args) {
int [][]a=new int[5][5]; //法1
a[0][0]=1;
a[0][1]=2;
a[0][2]=3;
int b[][] = new int[][]{ // 法2
{1,2,3},
{4,5,6},
{7,8,9}
};
}
}
4.Arrays类
Arrays是针对数组的工具类,可以进行 排序,查找,复制填充等功能。 大大提高了开发人员的工作效率。
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
package day2;
import java.lang.reflect.Array;
import java.util.Arrays;
public class Demo15 {
public static void main(String[] args) {
int [] a=new int[]{12,2,5,4};
//转换为字符串 toString
System.out.println(Arrays.toString(a));
System.out.println("_______________");
// 数组复制 copyOfRange
int []b=new int[a.length];
b=Arrays.copyOfRange(a,0,4);
// copyOfRange(int[] original, int from, int to)
// 第一个参数表示源数组
// 第二个参数表示开始位置(取得到)
// 第三个参数表示结束位置(取不到)
System.out.println(Arrays.toString(b));
System.out.println("_______________");
//排序 sort
Arrays.sort(b); //排序方法
System.out.println(Arrays.toString(b));
System.out.println("_______________");
//搜索 binarySearch //数组必须提前排好
System.out.println("数字12出现位置:"+Arrays.binarySearch(b ,12));
System.out.println("_______________");
//判断是否相同 equals
int []c=new int[a.length];
c=Arrays.copyOfRange(a,0,4);
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(c));
System.out.println(Arrays.equals(a,c));
System.out.println("_______________");
// 填充 fill
Arrays.fill(a,23);
System.out.println(Arrays.toString(a));
}
}
[12, 2, 5, 4]
_______________
[12, 2, 5, 4]
_______________
[2, 4, 5, 12]
_______________
数字12出现位置:3
_______________
[12, 2, 5, 4]
[12, 2, 5, 4]
true
_______________
[23, 23, 23, 23]
5.选择和冒泡排序
选择法排序的思路:
把第一位和其他所有的进行比较,只要比第一位小的,就换到第一个位置来
比较完后,第一位就是最小的
然后再从第二位和剩余的其他所有进行比较,只要比第二位小,就换到第二个位置来
比较完后,第二位就是第二小的
以此类推
冒泡法排序的思路:
第一步:从第一位开始,把相邻两位进行比较
如果发现前面的比后面的大,就把大的数据交换在后面,循环比较完毕后,最后一位就是最大的
第二步: 再来一次,只不过不用比较最后一位
以此类推
package day3;
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) {
int [] a=new int[]{11,3,5,90,56};
//选择排序
for (int j = 0; j < a.length-1; j++) {
for (int i = j+1; i < a.length; i++) {
if (a[i] < a[j]) {
int b = a[j];
a[j] = a[i];
a[i] = b;
}
}
}
System.out.println(Arrays.toString(a));
System.out.println("-------------------");
int []a1=new int[]{11,3,5,90,56};
//冒泡排序
for (int j = 0; j < a1.length; j++) {
for (int i = 0; i < a1.length-1-j; i++) {
if (a1[i] > a1[i + 1]) {
int temp = a1[i + 1];
a1[i + 1] = a1[i];
a1[i] = temp;
}
}
}
System.out.println(Arrays.toString(a1));
}
}
[3, 5, 11, 56, 90]
-------------------
[3, 5, 11, 56, 90]
6.面向对象
1.引用和对象
以类的方式组织代码,以对象组织(封装)代码
Ietm hero =new Ietm();
这个hero 叫做引用
Ietm()叫做对对象
把对象给应用,称为把引用指向对象,可以这样想,引用好比是指针,而对象是一空间的地址。指针指向地址,
引用只能指向一个对象,但是对象可以有多个引用,好比多个指针都能指向这一个空间
引用在栈内存,对象在堆内存
package day3;
public class Demo02 {
int a=10;
public static void main(String[] args) {
Demo02 d1=new Demo02();
Demo02 d2=d1;
Demo02 d3=d1;
Demo02 d4=d1;
Demo02 d5=d1;
d1.a=14;// 改变d1指向对象的值
System.out.println(d1.a);
System.out.println(d2.a);
System.out.println(d3.a);
System.out.println(d4.a);
System.out.println(d5.a);
// 对象只有一个,d1,d2,d3,d4,d5 都指向这个对象
}
}
14
14
14
14
14
2.构造器
java中的构造方法是一种特殊类型的方法,用于初始化对象。
Java构造函数在对象创建时被调用。 它构造值,即提供对象的数据,这是为什么它被称为构造函数。
构造器特征:
-
名称和类名相同
-
没有返回值
package day3;
public class Demo03 {
public Demo03(){ //无参的构造方法,如果不写,系统会默认写一个
System.out.println("无参构造");
}
//构造方法也可以重载,写出多个构造方法 如果定义有参的构造方法,系统默认的构造方法会消失
public Demo03(String s){
System.out.println("有参构造");
}
public static void main(String[] args) {
Demo03 demo03 =new Demo03();
Demo03 demo031 =new Demo03("c");
}
}
无参构造
有参构造
package day3;
public class Demo04 {
public Demo04(int a){
System.out.println("有参构造");
}
public static void main(String[] args) {
//定义有参的构造方法后,系统默认的构造方法会消失
Demo04 demo04 =new Demo04();
}
}
3.This关键字
1.在构造器中解决重名问题
package day3;
public class Demo04 {
int fg;
public Demo04(int fg){
this.fg=fg;
//左边是成员变量,右边是形参
}
public static void main(String[] args) {
}
}
2.this 可以表示当前对象
package day3;
public class Demo04 {
public Demo04(){
System.out.println(this); //输出当前对象的地址值
}
public static void main(String[] args) {
Demo04 demo04 = new Demo04();
System.out.println(demo04);
// 输出的地址值相同
}
}
day3.Demo04@1b6d3586
day3.Demo04@1b6d3586
3.调用其他构造方法
package day3;
//可以在一个构造器中调用另一个构造器
//调用另一个构造器,括号里面是另一个构造器的参数名称,例如 调用无参构造器 this();
public class Demo05 {
int a;
public Demo05 (int a){
this.a=a;
System.out.println("一个参的构造方法");
}
public Demo05 (int a,String s){
this(a); // 会调用上一个构造方法,括号里面是上一个构造器的参数名称
System.out.println("两个参的构造方法");
}
public static void main(String[] args) {
Demo05 demo05 =new Demo05(1,"hello");
System.out.println(demo05.a);
}
}
一个参的构造方法
两个参的构造方法
1
4.访问修饰符
那么什么情况该用什么修饰符呢?
从作用域来看,public能够使用所有的情况。 但是大家在工作的时候,又不会真正全部都使用public,那么到底什么情况该用什么修饰符呢?
-
属性通常使用private封装起来
-
方法一般使用public用于被调用
-
会被子类继承的方法,通常使用protected
-
package用的不多,一般新手会用package,因为还不知道有修饰符这个东西
再就是作用范围最小原则
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了
5.static
静态变量/属性属于类,不属于对象,即使对象有无数个,但静态变量/方法的内存空间只有一个,
所以静态变量/属性又称类变量,类方法
一个类下两个静态方法或者非静态方法可以相互访问,不要实例化
非静态方法也可以直接访问静态方法。
静态方法不能直接访问非静态的方法,因为静态方法和类是同时加载的,而方法在后面,需要实例化,所以静态方法不能直接访问非静态的方法
如果有继承关系,子类可以通过父类名称.静态方法 来访问父类的静态方法。
静态方法/变量也可以被别的类实例化,不过值会是原来类的值
1.静态方法中 调用 静态方法/属性
方法1. 类名.方法/属性名称
方法2. 方法/属性名称
方法3. 实例化对象,引用.方法/属性名称 (不推荐)
package day3;
public class Demo06 {
public static int a;
public static void fun(){
}
public static void main(String[] args) {
// 方法1:类名.方法/属性名称
System.out.println(a);
Demo06.fun();
//方法2:方法/属性名称
System.out.println(a);
fun();
//方法3:实例化对象,引用.方法/属性名称 (不推荐)
Demo06 demo06 =new Demo06();
System.out.println(demo06.a);
demo06.fun();
}
}
2.静态方法中 调用 非静态方法/属性
只能实例化对象,引用.方法/属性名称
package day3;
public class Demo07 {
int a;
public void fun(){
System.out.println("hello");
}
public static void main(String[] args) {
Demo06 demo06 =new Demo06();
demo06.fun();
demo06.a=1;
}
}
3.非静态方法 调用 静态方法/属性
方法1. 类名.方法/属性名称
方法2. 方法/属性名称
方法3. 实例化对象,引用.方法/属性名称 (不推荐)
package day3;
public class Demo08 {
static int a;
public static void fun(){
System.out.println("静态方法");
}
public void fun1(){
//方法1;类名.方法/属性名称
Demo06.a=1;
Demo06.fun();
// 方法二:方法/属性名称
a=1;
fun();
//方法3;实例化对象,引用.方法/属性名称 (不推荐)
Demo06 demo06 =new Demo06();
demo06.a=1;
demo06.fun();
}
}
4.非静态方法 调用 非静态方法/属性
方法1. 方法/属性名称
方法2. 实例化对象,引用.方法/属性名称 (不推荐)
package day3;
public class Demo09 {
int a;
void fun(){
System.out.println("fun方法");
}
void fun1(){
// 不能通过类名+方法名/属性名来访问
Demo09.a =1;
Demo09.f();
// 法1;直接访问 方法/属性名称
a=1;
fun();
//法2;实例化对象,引用.方法/属性名称 (不推荐)
Demo09 demo09 =new Demo09();
demo09.a=1;
demo09.fun();
}
}
6.属性初始化
1.普通属性的初始化
- 声明该属性的时候初始化
- 构造方法中初始化
- 代码块初始化
package day3;
public class Demo10 {
public String name="dev"; //定义时初始化
{
name ="dev"; //代码块初始化
}
public Demo10(){
name="dev"; //构造方法初始化
}
}
2.类属性初始化
-
声明该属性的时候初始化
-
代码块初始化
package day3;
//注意 普通的代码块里面可以初试化静态属性
但是 静态代码块不能初试化普通的属性.
public class Demo11 {
public static String name="dev"; //定义时初始化
{
name ="dev"; //代码块初始化
}
public Demo11(){
name="devl"; /* 类属性不用构造器来初始化
可能是使用类属性不用实例化 */
}
public static void main(String[] args) {
Demo11 demo11=new Demo11();
System.out.println(name);
}
}
devl
3.三种初始化方法执行顺序
package day3;
public class Demo12 {
String name="初始化";
{
System.out.println(name);
name="代码块";
}
public Demo12(){
System.out.println(name);
name="构造器";
}
public static void main(String[] args) {
Demo12 demo12 =new Demo12();
System.out.println(demo12.name);
}
}
初始化
代码块
构造器
7.封装
面对对象的三大特性:封装,继承,多态
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,并通过该类提供的方法来实现对隐藏信息的操作和访问。(简单的说就是隐藏对象的信息,留出访问的接口),
这样保护了数据的安全性,比如,LOL中设置等级上限数据为16级,如果不把数据设为private,谁都可以访问,那么一些外挂就会调高等级的上限。
特点:
-
只能通过规定的方法访问数据
-
隐藏类的实例细节,方便修改和实现
实现封装的步骤:
1.修改属性的可见性来限制对属性的访问,比如修改为private
2.对于每个属性提供对外的方法
package day4;
public class Demo01 {
private String name;
private int age;
public int getAge(){ //通过方法来返回属性
return age;
}
public void setAge(int age){ //通过方法来修改属性
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}
package day4;
public class Demo02 {
public static void main(String[] args) {
Demo01 demo01 =new Demo01();
demo01.setAge(18);
demo01.setName("dev");
System.out.println(demo01.getName()+" "+demo01.getAge());
}
}
dev 18
8.继承
exends 继承父类的方法和属性
父类的方法和属性是:
- public:谁都可以访问
- protected:同一个包内或子类才可以访问
- 默认:同一个包内能访问。
- private:不能够访问(但是在子类的构造器中,可以调用父类的构造函数,让子类有父类私有的属性,但是仍然不可以访问私有的属性,只是子类的属性有了父类的私有属性而已,但子类不可访问)
package day4;
public class Demo03 {
public int age;
public void fun(){
System.out.println("父类方法");
}
}
package day4;
public class Demo04 extends Demo03{
String name;
public static void main(String[] args) {
Demo04 demo04 =new Demo04();
demo04.name="dev";
demo04.age=16;
demo04.fun();
}
}
父类方法
9.super关键字
由于子类不能继承父类的构造方法,因此,如果要调用父类的构造方法,可以使用 super 关键字
1.调用父类的方法和属性
在子类的方法中可以调用父类的方法和变量,只能出现在子类的方法或者构造器中
package day4;
public class Demo06 {
String s="父类属性";
public void fun(){
System.out.println("父类方法");
}
}
package day4;
public class Demo07 extends Demo06{
public void fun1(){
super.fun(); //在子类方法中可以用super来访问父类方法
System.out.println(super.s); //可以直接调用父类的属性
}
public void fun(){
System.out.println("子类方法");
}
public void display(){
fun();
super.fun(); //当子类和父类方法重名时,用用super来访问父类方法
}
public static void main(String[] args) {
Demo07 demo07 =new Demo07();
demo07.fun1();
demo07.display();
}
}
父类方法
父类属性
子类方法
父类方法
2.调用父类的构造器
在子类构造器中默认调用父类的无参构造 即 super()(不会写出),必须放在子类构造器的第一行。(super和this不能同时用,因为都要放在第一行,如果父类没有无参构造,就会报错。
package day4;
public class Demo08 {
public Demo08(){
System.out.println("父类无参构造");
}
}
public class Demo09 extends Demo08 {
public Demo09(){
System.out.println("子类无参构造");
}
public static void main(String[] args) {
Demo09 demo09 =new Demo09();
}
}
父类无参构造
子类无参构造
package day4;
public class Demo08 {
// public Demo08(){ 去除父类无参构造,就会报错
// System.out.println("父类无参构造");
// }
public Demo08(int a){
System.out.println("父类有参构造");
}
}
package day4;
//父类有一个有参构造,所以默认无参构造被删除,子类构造器中的super()就会找不到父类的无参构造,就会报错,解决的方法是在
// 1.父类写一个无参构造
// 2.子类的有参构造器调用中父类的有参构造
public class Demo09 extends Demo08 {
// public Demo09(){ 父类无有参构造就会报错
// System.out.println("子类无参构造");
// }
public Demo09(int a){ //
super(a);
System.out.println("子类有参构造");
}
public static void main(String[] args) {
Demo09 demo09 =new Demo09(2);
}
}
9.重写
子类可以继承父类的对象方法
在继承后,重复提供该方法,就叫做方法的重写
又叫覆盖 override
package day4;
public class Demo10 {
public void eat(){
System.out.println("父类吃饭");
}
}
package day4;
public class Demo11 extends Demo10{
public void eat(){
System.out.println("子类吃饭");
}
public static void main(String[] args) {
Demo11 demo11 =new Demo11();
demo11.eat();
}
}
子类吃饭
10.多态
多态存在的三个前提:
-
要存在继承关系
-
子类要重写父类的方法
-
子类的对象传给父类的引用
注意: 多态是方法的多态,属性(变量)没有多态
子类独有的方法不能被父类的引用来执行
能不能执行主要看左边的类型,除非这个方法被重写了
多态的好处:
- 提高的代码的维护性(继承保证,即可以通过改写父类属性与方法同义改写子类共有属性与方法)
- 提高了代码的扩展性(由多态保证,即父类为形式参数,接收任意子类对象,在开发的时候用多态,扩展性
package day4;
public class Demo12 {
public void fun(){
System.out.println("父类的方法");
}
public static void nice(){
System.out.println("父类的静态方法");
}
}
package day4;
public class Demo13 extends Demo12{
public void fun(){
System.out.println("重写父类的方法");
}
public void fun1(){
System.out.println("子类的方法");
}
public static void nice1(){
System.out.println("子类的静态方法");
}
public static void main(String[] args) {
Demo13 demo13 =new Demo13();//子类引用 子类对象
Demo12 demo12= new Demo13();//父类引用 子类对象
demo13.fun(); // 子类访问重写父类的方法
demo12.fun();// 多态 访问重写父类的方法
// demo12.fun1(); //子类的方法,父类不能访问
//demo12.nice1();//子类的方法,父类不能访问
demo12.nice(); // 可以访问父类静态方法
}
}
重写父类的方法
重写父类的方法
父类的静态方法
package day4;
public class Item {
void fun(){
System.out.println("父类");
}
static void fun1(Item item){
item.fun(); //根据传入的不同子类对象,来输出不同的值
}
public static void main(String[] args) {
Item a1= new Son1();
Item a2=new Son2();
a1.fun(); //根据不同的对象,输出不同的值
a2.fun();
System.out.println("____________");
fun1(new Son1());
fun1(new Son2());
}
}
package day4;
public class Son1 extends Item{
void fun(){
System.out.println("子类1");
}
}
package day4;
public class Son2 extends Item{
void fun(){
System.out.println("子类2");
}
}
子类1
子类2
____________
子类1
子类2
11.Instanceof关键字
左边是对象,右边是类,判断二者是否有继承关系
12.抽象类
在类中声明一个方法,这个方法没有实现体,是一个“空”方法
这样的方法就叫抽象方法,使用修饰符“abstract"
抽象abstract:exends继承抽象类,抽象类不能被实例化,继承他的子类可以实例化,抽象类中可以有非抽象的方法,但有抽象方法的类必须为抽象类,继承抽象类的子类必须重写父类的抽象方法,否则自己成为抽象类,一个类只能继承一次抽象类。
接口interface:Implements得到接口,接口中全都是抽象类,子类必须全部实现,一个类可以实现多个接口,接口不能实例化(接口类型的变量是指可以把实现接口的子类的对象传递给这个变量)
抽象类和接口所反映的设计理念是不同的,抽象类所代表的是“is-a”的关系,而接口所代表的是“like-a”的关系。
抽象类和接口在应用层面差异不大,都是重写了抽象方法,但是由于抽象类可以存放非抽象方法,我们可以选择继承或不继承,避免在子类中重复书写,提高了代码有复写性,但是抽象类只能继承一次,大大降低了他的使用空间。
而接口可以继承无数多个,那么提高了代码的灵活性,可以让很多人写一个项目,但同时也让代码量增多,显得臃肿。
抽象类和接口的区别:
- 区别1:
子类只能继承一个抽象类,不能继承多个
子类可以实现多个接口- 区别2:
抽象类可以定义:
public,protected,package,private
静态和非静态属性
final和非final属性- 但是接口中声明的属性,只能是
public
静态
final的
即便没有显式的声明
- 但是接口中声明的属性,只能是
- 区别2:
package day4;
public abstract class Person { //定义抽象类
public abstract void eat(); //抽象方法
// 抽象类可以没有抽象方法,但是被定义为抽象类,就不能被实例化
public void fun(){ //抽象类可以有普通方法
}
}
package day4;
public class Man extends Person{
public void eat(){
System.out.println("男人吃");
}
}
13.接口
接口规范了一些方法,接口可以多继承,接口没有实例化和构造器,必须重写接口的所有方法
package day4;
public interface Animal { //接口
void eat(); //抽象方法
default void fun(){ //默认方法,不要求子类必须实现
System.out.println("( •̀ ω •́ )y");
}
}
package day4;
public class Cat implements Animal{
@Override
public void eat() { //实现接口的方法
}
}
接口的默认方法:
假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法
通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类
14.内部类
内部类分为四种:
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。——《Think in java》
也就是说内部类拥有类的基本特征。(eg:可以继承父类,实现接口。)在实际问题中我们会遇到一些接口无法解决或难以解决的问题,此时我们可以使用内部类继承某个具体的或抽象的类,间接解决类无法多继承引起的一系列问题。(注:内部类可以嵌套内部类,但是这极大的破坏了代码的结构,这里不推荐使用。)
package day4;
public class Outer {
private int a=100;
private void out(){
System.out.println("外部类");
}
public class Inner{ //1.成员内部类
public void in(){
System.out.println("内部类");
}
}
// 一个java类中可以有多个内部类,但是只能有一个public class
public static class Inner2{ //2.静态内部类
public void in(){
System.out.println("内部类");
}
}
public void fun(){
class nice{ // 3.局部内部类,在方法中定义
}
}
public static void main(String[] args) {
Outer outer =new Outer(); //实例化外部类
Inner inner=outer.new Inner(); //通过外部类来实例化内部类
inner.in(); //访问内部类方法
new Happy().fun(); // 匿名内部类 不用将实例保持到变量中
}
}
class Happy {
public void fun() {
System.out.println("");
}
}
15.对象转型
没有继承关系的相互转换必定出错
1.向上转型 子类转父类
子类继承了父类的方法,所以说子类对象里面有父类的方法,
所以 父类的引用 指向 子类的对象,这个引用可以使用子类继承了父类的方法,子类独有的方法不可以使用。
package day4;
public class Father {
public void fun(){
System.out.println("父亲开心");
}
}
package day4;
public class Son extends Father {
public void fun(){ //重写父类方法
System.out.println("儿子开心");
}
public void Happy(){ //子类独有的方法
}
public static void main(String[] args) {
Father father=new Son(); //向上转型,父类引用,子类对象
father.fun();
// father.Happy();//用不了子类独有的方法
}
}
儿子开心
2.向下转型 父类转子类
父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是 转换有风险,风险自担。
Father father=new Father();
Son son =new Son();
son=(Son) father;
Exception in thread "main" java.lang.ClassCastException: day4.Father cannot be cast to day4.Son
at day4.Son.main(Son.java:12)
出现了类型转换异常
//只要加一步就可以了
Father father=new Father();
Son son =new Son();
father=son;
son=(Son) father;
3.接口转成类 向下转型
接口必须指向那个实现它的类,才可以转换成功
package day4;
public interface T1 {
void fun();
}
package day4;
public class AD implements T1{
@Override
public void fun() {
}
}
package day4;
public class AP implements T1{
@Override
public void fun() {
}
public void Happy(){} //AP子类独有方法
public static void main(String[] args) {
AD ad=new AD();
AP ap =new AP();
T1 t1 =ap; //类转接口 向上转型
t1.fun(); //共有的方法可以用
//t1.Happy();// 独有方法不行
ap=(AP) t1; //t1实际指向ap,可以转换成功
ad=(AD) t1; // ad虽然实现了接口,但t1没有指向它,所以转换不了
}
}
4.类转换成接口 向上转型
代码同上
16.Object 类
Object类是所有类的父类
1.toString()
返回当前对象的字符串表达
package day4;
public class Demo01 {
private String name;
private int age;
public static void main(String[] args) {
Demo01 demo01 =new Demo01();
System.out.println(demo01.toString());
}
}
day4.Demo01@1b6d3586
2.finalize()
package day4;
//当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件,当它被垃圾回收的时候,它的finalize() 方法就会被调用。finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
public class Demo01 {
public void finalize(){
System.out.println("正在被回收");
}
public static void main(String[] args) {
Demo01 demo01;
for (int i = 0; i <1000000 ; i++) {
demo01=new Demo01();
}
}
}
3.equals()
package day4;
public class Demo01 {
// equals() 用于判断两个对象是否时同一个
int a;
public static void main(String[] args) {
Demo01 demo01 =new Demo01();
Demo01 demo011 =demo01;
System.out.println(demo01.equals(demo011));
}
}
true
17.final关键字
1.final 修饰类,这个类不可被继承
package day4;
public final class Demo01 {
public static void main(String[] args) {
}
}
package day4;
public class Demo02 extends Demo01{ // 不能被继承
}
2.final 修饰方法,这个方法不能被重写
package day4;
public class Demo01 {
public final void fun(){
}
public static void main(String[] args) {
}
}
package day4;
public class Demo02 extends Demo01{
public void fun(){ //不能重写父类方法
}
}
3.final 修饰变量,这个变量只有一次的赋值机会
final int a=9;
a=10; //只能赋值一次
4.final 修饰引用,该引用只有一次指向对象的机会
final Demo01 demo01;
demo01=new Demo01();
demo01=new Demo01();//只能赋值一次
7.数字与字符串
Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表。
很多人会有疑问,既然Java中为了提高效率,提供了八种基本数据类型,为什么还要提供包装类呢?
这个问题,其实前面已经有了答案,因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。
1.拆箱和装箱
package day5;
public class Demo01 {
public static void main(String[] args) {
int i=10;
Integer integer =new Integer(i); //基本类型转换成封装类型
int i2= integer.intValue();//封装类转换为基本类型
}
}
package day5;
public class Demo02 {
public static void main(String[] args) {
//不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫装箱
int i=10;
Integer integer=i; //自动装箱
//不需要调用Integer的intValue方法,通过=就自动转换成int类型,就叫拆箱
int i2=integer; //自动拆箱
}
}
2.字符串转换
1.数字转字符串
package day5;
public class Demo03 {
public static void main(String[] args) {
int a=123;
String s =String.valueOf(a); //方法1 使用String类的静态方法valueOf
//方法2 先把基本类型装箱为对象,然后调用对象的toString
Integer integer =a;
String s1= integer.toString();
System.out.println(s);
System.out.println(s1);
}
}
123
123
2.字符串转数字
package day5;
public class Demo04 {
public static void main(String[] args) {
//调用Integer的静态方法parseInt
String s="1234";
int i = Integer.parseInt(s);
System.out.println(i);
}
}
1234
3.数学方法
java.lang.Math提供了一些常用的数学运算方法,并且都是以静态方法的形式存在
1.四舍五入
double a=3.14159265;
System.out.println(Math.round(a)); //四舍五入
3
2.随机数
System.out.println(Math.random());//0-1取随机数,1取不到
System.out.println(Math.random()*10);//0-10取随机数 10取不到
System.out.println((int) (Math.random()*10));//0-10取随机整数 10取不到
0.9524663719426366
5.740819659348589
7
3.开方
System.out.println(Math.sqrt(16));//开方
4.0
4.绝对值
System.out.println(Math.abs(-7));//绝对值
7
10
1131115111
5
21134
5.次方
System.out.println(Math.pow(2,4)); //2的4次方
16.0
6.π
System.out.println(Math.PI);//π
3.141592653589793
7.自然对数e
System.out.println(Math.E);//自然对数e
2.718281828459045
4.格式化输出
如果不使用格式化输出,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐
使用格式化输出,就可以简洁明了
%s 表示字符串
%d 表示数字
%n 表示换行
package day5;
public class Demo06 {
public static void main(String[] args) {
String s="我";
int a=1;
String s1="可爱娇小的女孩子";
String s2="%s喜欢%d个%s%n";
System.out.printf(s2,s,a,s1); //使用格式化输出,%s表示字符串,%d表示数字
// printf和format能够达到一模一样的效果
System.out.format(s2,s,a,s1);
}
}
我喜欢1个可爱娇小的女孩子
我喜欢1个可爱娇小的女孩子
package day5;
public class Demo07 {
public static void main(String[] args) {
int a=1234;
System.out.format("%d%n",a);//输出a
System.out.format("%8d%n",a);//输出8位,左对齐
System.out.format("%-8d%n",a);//输出8位,右对齐
System.out.format("%08d%n",a);//输出8位,其余补0
System.out.format("%,8d%n",a*1000);//千位分隔符
System.out.format("%4.2f",Math.E);//输出4位,小数点后两位
}
}
1234
1234
1234
00001234
1,234,000
2.72
5.字符
char c1 = 'a';
Character c = c1; //自动装箱
c1 = c;//自动拆箱
package day5;
public class Demo08 {
public static void main(String[] args) {
System.out.println(Character.isLetter('a'));//判断是否为字母
System.out.println(Character.isDigit('a')); //判断是否为数字
System.out.println(Character.isWhitespace(' ')); //是否是空白
System.out.println(Character.isUpperCase('a')); //是否是大写
System.out.println(Character.isLowerCase('a')); //是否是小写
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
//String a = 'a'; //不能够直接把一个字符转换成字符串
String a2 = Character.toString('a'); //转换为字符串
}
}
true
false
true
false
true
A
a
6.字符串
1.创建字符串
String s="abc";// 方法1 直接创建
String s1= new String("abc");//方法2:实例化创建
char []c={'a','b','c'};
String s2=new String(s1);//方法3:用字符数组来创建
2.final和immutable
String 被修饰为final,所以是不能被继承的
immutable 是指不可改变的
比如创建了一个字符串对象
String garen =“盖伦”;
不可改变的具体含义是指:
不能增加长度
不能减少长度
不能插入字符
不能删除字符
不能修改字符
一旦创建好这个字符串,里面的内容 永远 不能改变
String 的表现就像是一个常量
3.字符串的操作
1.字符串的长度 length()
String s="abc";
System.out.println(s.length());//获取长度
3
2.获取字符 charAt()
//charAt(int index)获取指定位置的字符
String s1="abcd";
char c=s1.charAt(1);//获取位置1的字符
System.out.println(c);
b
3.获取的字符数组 toCharArray()
String s2="abcd";
char [] c2=s2.toCharArray();//转化为对应字符数组
for (char c1 : c2) {
System.out.println(c1);
}
a
b
c
d
4.截取子字符串 subString()
String s3="abcdefg";
String s4=s3.substring(2,4);//截取位置2到位置4的子字符串,左闭右开
System.out.println(s4);
cd
5.分隔split()
String s5 ="123134156189";
String [] strings=s5.split("1");//根据字符串1来分割4个子字符串
for (String string : strings) {
System.out.println(string);
}
23
34
56
89
6.去掉首尾空格trim()
String s6 =" 123abc ";
System.out.println(s6.trim());//去掉首尾空格
123abc
7.大小写 toLowerCase() toUpperCase()
String s7 ="AbcDEF";
System.out.println(s7.toLowerCase());//全部变成小写
System.out.println(s7.toUpperCase());//全部变成大写
abcdef
ABCDEF
8.定位 indexOf contains
indexOf 判断字符或者子字符串出现的位置
contains 是否包含子字符串
String s8="abc123";
System.out.println(s8.indexOf('b'));//判断字符出现位置
System.out.println(s8.indexOf("ab"));//判断子字符串出现位置
System.out.println(s8.contains("12"));//判断是否包含子字符串
1
0
true
9.替换
replaceAll 替换所有的
replaceFirst 只替换第一个
String s9="1213";
System.out.println(s9.replaceAll("1","a"));//把1都替换成a
System.out.println(s9.replaceFirst("1","a"));//只替换第一个
a2a3
a213
10.字符串比较
String s10="123rt";
String s11="123rt";
String s12=new String("123rt");
System.out.println(s10==s12);//没有指向同一个对象
System.out.println(s10==s11);//编译器发现有现成的"123rt"空间,所以直接指向
String s13="123RT";
System.out.println(s10.equalsIgnoreCase(s12));//判断内容是否相等,忽略大小写
11.是否以子字符串开始或者结束
startsWith //以…开始
endsWith //以…结束
String s14="12 dfg ty";
System.out.println(s14.startsWith("12"));//以12为子字符串开始
System.out.println(s14.endsWith("ty"));//以ty为子字符串结束
true
true
4.StringBuffer
StringBuffer是可变长的字符串
StringBuffer 可以对字符串本身进行操作
为什么StringBuffer可以变长?
和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度 比如说new StringBuffer(“the”),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了
String s15="1234abcd";
StringBuffer sb=new StringBuffer(s15);//创建StringBuffer
sb.append("AD");//在最后追加字符串
System.out.println(sb);
sb.delete(7,10);//删除7到10的字符
System.out.println(sb);
sb.insert(2,"Cd");//在位置2插入Cd
System.out.println(sb);
sb.reverse(); //反转
System.out.println(sb);
System.out.println(sb.length()); //内容长度
System.out.println(sb.capacity());//总空间
String s15="1234abcd";
StringBuffer sb=new StringBuffer(s15);//创建StringBuffer
sb.append("AD");//在最后追加字符串
System.out.println(sb);
sb.delete(7,10);//删除7到10的字符
System.out.println(sb);
sb.insert(2,"Cd");//在位置2插入Cd
System.out.println(sb);
sb.reverse(); //反转
System.out.println(sb);
System.out.println(sb.length()); //内容长度
System.out.println(sb.capacity());//总空间
8.日期
1.获取当前的系统时间
package day6;
import java.util.Date;
public class Demo01 {
public static void main(String[] args) {
Date date = new Date();//方法一
System.out.println(date);
System.out.println("___________");
//方法二:getTime() 得到一个long型的整数
//这个整数代表 从1970.1.1 08:00:00:000 开始 每经历一毫秒,增加1
System.out.println(date.getTime());
// 方法三:System.currentTimeMillis()
//当前日期的毫秒数
//new Date().getTime() 和 System.currentTimeMillis() 是一样的
//不过由于机器性能的原因,可能会相差几十毫秒,毕竟每执行一行代码,都是需要时间的
System.out.println(System.currentTimeMillis());
}
}
Mon Sep 27 20:51:57 CST 2021
___________
1632747117662
1632747117678
2.日期格式化
1.日期转字符串
SimpleDateFormat 日期格式化类
package day6;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
public class demo02 {
public static void main(String[] args) {
/*
SimpleDateFormat 日期格式化类
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 s=sdf.format(d);
System.out.println("当前时间通过 yyyy-MM-dd HH:mm:ss SSS 格式化后的输出: "+s);
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd" );
String s1=sdf1.format(d);
System.out.println("当前时间通过 yyyy-MM-dd 格式化后的输出: "+s1);
}
}
当前时间通过 yyyy-MM-dd HH:mm:ss SSS 格式化后的输出: 2021-09-27 21:00:33 823
当前时间通过 yyyy-MM-dd 格式化后的输出: 2021-09-27
2.字符串转日期
模式(yyyy/MM/dd HH:mm:ss)需要和字符串格式保持一致,如果不一样就会抛出解析异常ParseException
package day6;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo03 {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String str = "2016/1/5 12:12:12";
try {
Date d = sdf.parse(str);
System.out.printf("字符串 %s 通过格式 yyyy/MM/dd HH:mm:ss %n转换为日期对象: %s", str, d.toString());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
字符串 2016/1/5 12:12:12 通过格式 yyyy/MM/dd HH:mm:ss
转换为日期对象: Tue Jan 05 12:12:12 CST 2016
3.Calendar
在实际项目当中,我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年。。。。某些网站会记录下用户登陆的时间,比如银行的一些网站,对于这些经常需要处理的问题,Java中提供了Calendar这个专门用于对日期进行操作的类。
package day6;
import java.util.Calendar;
import java.util.Date;
public class Demo04 {
public static void main(String[] args) {
//采用单例模式获取日历对象Calendar.getInstance();
Calendar c = Calendar.getInstance();
//通过日历对象得到日期对象
Date d = c.getTime();
System.out.println(d);//输出当下时间
Date d2 = new Date(0);
c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00
System.out.println(d2);//输出调整后的时间
}
}
Mon Sep 27 21:44:48 CST 2021
Thu Jan 01 08:00:00 CST 1970
package day6;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Demo05 {
private static String format(Date time) {
return sdf.format(time);
} //用来格式化输出的
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
//该类被abstract所修饰,说明不能通过new的方式来获得实例,对此,Calendar提供了一个类方法getInstance,以获得此类型的一个通用的对象,
// getInstance方法返回一个Calendar对象(该对象为Calendar的子类对象),其日历字段已由当前日期和时间初始化:
Calendar calendar =Calendar.getInstance();
Date now = calendar.getTime();
System.out.println(calendar.getTime());//输出当前时间
System.out.println(format(calendar.getTime()));//格式化当前时间
// 三个月后的今天
calendar.setTime(now); //设置当前时间
calendar.add(Calendar.MONTH,3);//在原有时间上加3个月
System.out.println("三个月后"+calendar.getTime());//输出三个月后时间
System.out.println("三个月后"+format(calendar.getTime()));//格式化输出
//去年的今天
calendar.setTime(now); //设置当前时间
calendar.add(Calendar.YEAR,-1);//在原有时间减一年
System.out.println("一年前"+calendar.getTime());//输出一年前时间
System.out.println("一年前"+format(calendar.getTime()));//格式化输出
//上个月的第5天
calendar.setTime(now); //设置当前时间
calendar.add(Calendar.MONTH,-1);//在原有时间减一个月
calendar.set(Calendar.DATE,5);//设置上月日期为
System.out.println("上个月的第5天"+calendar.getTime());//输出上个月的第5天时间
System.out.println("上个月的第5天"+format(calendar.getTime()));//格式化输出
//输出这个月第1天
calendar.setTime(now); //设置当前时间
calendar.set(Calendar.DATE,1);
System.out.println("这个月第1天"+calendar.getTime());//输出这个月第1天时间
System.out.println("这个月第1天"+format(calendar.getTime()));//格式化输出
//输出去年的5月份这个时间
calendar.setTime(now); //设置当前时间
calendar.add(Calendar.YEAR,-1);//在原有时间减一年
calendar.add(Calendar.MONTH,-4);//在原有时间减一个月
System.out.println("去年的5月份这个时间"+calendar.getTime());//输出去年的5月份这个时间时间
System.out.println("去年的5月份这个时间"+format(calendar.getTime()));//格式化输出
}
}
Mon Sep 27 21:42:01 CST 2021
2021-09-27 21:42:01
三个月后Mon Dec 27 21:42:01 CST 2021
三个月后2021-12-27 21:42:01
一年前Sun Sep 27 21:42:01 CST 2020
一年前2020-09-27 21:42:01
上个月的第5天Thu Aug 05 21:42:01 CST 2021
上个月的第5天2021-08-05 21:42:01
这个月第1天Wed Sep 01 21:42:01 CST 2021
这个月第1天2021-09-01 21:42:01
去年的5月份这个时间Wed May 27 21:42:01 CST 2020
去年的5月份这个时间2020-05-27 21:42:01
9.异常
1.异常的分类
1.可查异常: CheckedException
可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过
2.运行时异常RuntimeException
运行时异常RuntimeException指: 不是必须进行try catch的异常
常见运行时异常:
除数不能为0异常:ArithmeticException
下标越界异常:ArrayIndexOutOfBoundsException
空指针异常:NullPointerException
在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误
Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。
3.错误Error
指的是系统级别的异常,通常是内存用光了
在默认设置下,一般java程序启动的时候,最大可以使用16m的内存
如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError
与运行时异常一样,错误也是不要求强制捕捉的
异常 的类
2.异常处理
异常处理常见手段: try catch finally throws
1.单异常捕捉
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();//打印错误的栈信息
System.out.println("异常扑捉到了");
} finally {
System.out.println("无论是否捉到异常,finally都会执行");
}
java.lang.ArithmeticException: / by zero
at day7.Demo01.main(Demo01.java:8)
异常扑捉到了
无论是否捉到异常,finally都会执行
2.多异常捕捉
package day7;
public class Demo03 {
public static void main(String[] args) {
/*
ParseException 解析异常,日期字符串转换为日期对象的时候,有可能抛出的异常
OutOfIndexException 下标越界异常
OutOfMemoryError 内存不足
ClassCastException 类型转换异常
ArithmeticException 除数为零
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组下标越界
*/
//这一段代码有两个异常,但是只有捕捉到一个,就直接进入finally,
// 不会有,两个异常同时被捕捉
//而且catch 捕捉异常要从小到大
try {
int [] a={1,2,3};
System.out.println(a[10]); //ArrayIndexOutOfBoundsException 数组下标越界
System.out.println(1/0);// ArithmeticException 除数为零异常
}
catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
System.out.println("ArrayIndexOutOfBoundsException 捕捉到了");
}
catch (ArithmeticException e) {
e.printStackTrace();
System.out.println("ArithmeticException 捕捉到了");
}catch (Exception e){
System.out.println("Exception 捕捉到了");
}finally {
System.out.println("finally都会执行");
}
}
}
java.lang.ArrayIndexOutOfBoundsException: 10
at day7.Demo03.main(Demo03.java:19)
ArrayIndexOutOfBoundsException 捕捉到了
finally都会执行
package day7;
public class Demo04 {
public static void main(String[] args) {
//方法二:把多个异常,放在一个catch里统一捕捉
//这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型
// 同理 这一段代码有两个异常,但是只有捕捉到一个,就直接进入finally,
// 不会有,两个异常同时被捕捉
// 而且catch 捕捉异常要从小到大
try {
int [] a={1,2,3};
System.out.println(a[10]); //ArrayIndexOutOfBoundsException 数组下标越界
System.out.println(1/0);// ArithmeticException 除数为零异常
} catch (Exception e) {
if(e instanceof ArrayIndexOutOfBoundsException){
System.out.println("ArrayIndexOutOfBoundsException 捕捉到了");
}
if (e instanceof ArithmeticException){
System.out.println("ArithmeticException 捕捉到了");
}
} finally {
System.out.println("finally");
}
}
}
ArrayIndexOutOfBoundsException 捕捉到了
finally
3.throw和throws
package day7;
public class Demo05 {
public static void main(String[] args) {
try {
fun();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
System.out.println("捕捉到了");
}
}
//假设这个方法处理不了这个异常,所以要抛出异常
public static void fun() throws ArrayIndexOutOfBoundsException{
throw new ArrayIndexOutOfBoundsException(); //主动抛出异常,一般在方法中
}
}
java.lang.ArrayIndexOutOfBoundsException
at day7.Demo05.fun(Demo05.java:15)
at day7.Demo05.main(Demo05.java:7)
捕捉到了
3.自定义异常
package day7;
public class MyException extends Exception{ //自定义异常,只要继承Exception
public MyException (String messge){ //有参构造
super(messge); //调用父类的构造方法,不知道有什么用
}
@Override
public String toString() { //修改toString 方法后 直接输出MyException 的对象
//会输出没有萝莉/(ㄒoㄒ)/~~
return "没有萝莉/(ㄒoㄒ)/~~";
}
}
package day7;
public class Test {
public static void main(String[] args) {
String s="我爱萝莉";
String s1="我爱御姐";
try {
//fun(s);
fun(s1);
} catch (MyException e) {
System.out.println(e);
e.printStackTrace();
}
}
public static void fun(String s) throws MyException { // 方法抛出异常
if(!(s.contains("萝莉"))){
System.out.println(new MyException(s));
throw new MyException(s);//如果字符串不包含萝莉,那么抛出异常
}else{
System.out.println("有萝莉");
}
}
}
没有萝莉/(ㄒoㄒ)/~~
没有萝莉/(ㄒoㄒ)/~~
没有萝莉/(ㄒoㄒ)/~~
at day7.Test.fun(Test.java:18)
at day7.Test.main(Test.java:9)
10.IO流
1.File类
1.创建文件对象
package day8;
import java.io.File;
public class Demo01 {
public static void main(String[] args) {
File f1=new File("c:/123.txt");//方法1:指定盘符创建文件
System.out.println(f1.getAbsoluteFile());//获取绝对路径
File f2=new File("1234.txt");// 方法2:直接创建,会在之歌包下创建
System.out.println(f2.getAbsoluteFile());//获取绝对路径
File f3 =new File(f1,"12345.txt");//方法3:以f1为父目录来创建
System.out.println(f3.getAbsoluteFile());//获取绝对路径
}
}
c:\123.txt
D:\Code\JavaSE\1234.txt
c:\123.txt\12345.txt
2.文件操作常用方法
package day8;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo02 {
public static void main(String[] args) {
File f=new File("C:/Users/AnGeng/Desktop/123.txt");
System.out.println("当前文件名为"+f);
System.out.println("判断是否存在"+f.exists());
System.out.println("判断是否是文件夹"+f.isDirectory());
System.out.println("文件的长度"+f.length());
Date date =new Date(f.lastModified()); //lastModified() 返回的毫秒数,要先传给data 对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" ); //日期格式化输出
System.out.println(sdf.format(date));//文件最后修改时间
File f2=new File("C:/Users/AnGeng/Desktop/1234.txt");
System.out.println("命名结果"+f.renameTo(f2)); // 文件改名 必须确保f2不存在
System.out.println("当前文件名为"+f);
System.out.println("判断是否存在"+f.exists());
System.out.println("当前文件名为"+f2);
System.out.println("判断是否存在"+f2.exists());
// 改名后,f对象所指文件会变成 f2所指文件 f指向变为空
}
}
当前文件名为C:\Users\AnGeng\Desktop\123.txt
判断是否存在true
判断是否是文件夹false
文件的长度0
2021-09-29 16:25:52 605
命名结果true
当前文件名为C:\Users\AnGeng\Desktop\123.txt
判断是否存在false
当前文件名为C:\Users\AnGeng\Desktop\1234.txt
判断是否存在true
File f=new File("C:/Users/AnGeng/Desktop/123.txt");
System.out.println("当前文件名为"+f);
System.out.println("判断是否存在"+f.exists());
File f2=new File("C:/Users/AnGeng/Desktop/java/123456.txt");
System.out.println("当前文件名为"+f2);
System.out.println("判断是否存在"+f2.exists()); // 改名必须确保新的名字不存在
System.out.println("-----------");
System.out.println("命名结果"+f.renameTo(f2)); // 文件改名 文件改名可以改变文件的位置
System.out.println("当前文件名为"+f);
System.out.println("判断是否存在"+f.exists());
System.out.println("文件的长度"+f.length());
System.out.println("当前文件名为"+f2);
System.out.println("判断是否存在"+f2.exists());
当前文件名为C:\Users\AnGeng\Desktop\123.txt
判断是否存在true
当前文件名为C:\Users\AnGeng\Desktop\java\123456.txt
判断是否存在false
-----------
命名结果true
当前文件名为C:\Users\AnGeng\Desktop\123.txt
判断是否存在false
文件的长度0
当前文件名为C:\Users\AnGeng\Desktop\java\123456.txt
判断是否存在true
可以发现,文件重命名后,f不存在,而且貌似文件名没有改 但其实是f失去作用,f2开始有作用罢了
“重命名成功后,原文件会被删除”这点不敢苟同。从文件系统角度来看,重命名并不会删除文件,只是将文件改名;从程序内存角度来看,File对象与文件是一一对应的,同一个文件即使改了名字也应该对应不同的File对象。renameTo()方法需要传入的是一个新的File类对象,而不是一个新的String文件名,从这点可以很明显的看出JAVA的意图就是要用这个新的File对象来替换旧的File对象。改名成功后,虽然文件本身没有变化,但是原来那个名字的文件已经不存在了,所以原File对象自然也失去了意义。此时再去调用原File对象的exists()方法自然会得到false。所以可以看出,原文件并没有被删除,只是原File对象没有意义了。
3.文件夹操作方法
package day8;
import java.io.File;
import java.io.IOException;
public class Demo03 {
public static void main(String[] args) throws IOException {
File f=new File("C:/Users/AnGeng/Desktop/123");
System.out.println("判断是否是文件夹"+f.isDirectory());
String [] s= f.list(); //以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
for (String s1 : s) {
System.out.println(s1);
}
System.out.println("___________________");
File []file = f.listFiles();// 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
for (File file1 : file) {
System.out.println(file1);
}
System.out.println("___________________");
System.out.println(f.getParent()); // 以字符串形式返回获取所在文件夹
System.out.println("___________________");
File file1= f.getParentFile();// 以文件形式返回获取所在文件夹
System.out.println(file1);
System.out.println("___________________");
// 创建文件夹,如果父文件夹skin不存在,创建就无效
f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹
f.mkdirs();
System.out.println("___________________");
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
f.createNewFile();
// 所以创建一个空文件之前,通常都会创建父目录
f.getParentFile().mkdirs();
System.out.println("___________________");
// 列出所有的盘符c: d: e: 等等
System.out.println( f.listRoots());
// 刪除文件
System.out.println("判断是否是文件夹"+f.isDirectory());
System.out.println(f.delete()); // 删除文件夹,里面必须为空
// JVM结束的时候,刪除文件,常用于临时文件的删除
f.deleteOnExit();
}
}
判断是否是文件夹true
___________________
___________________
C:\Users\AnGeng\Desktop
___________________
C:\Users\AnGeng\Desktop
___________________
___________________
___________________
[Ljava.io.File;@1b6d3586
判断是否是文件夹true
true
2.流的定义
字节流和字符流操作的本质区别只有一个:字节流是原生的操作,字符流是经过处理后的操作。
画个图,字节流在操作时不会用到缓冲区,也就是不会用到内存,文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,看下图:
为什么要有字符流而不直接用字节流呢?
我相信有些读者心里肯定要问这个问题,我刚开始学习的时候也想过这个问题,为什么不直接用字节流解决呢,还非要搞个字符流出来呢。
我的理解就是字节流处理多个字节表示的东西的时候有可能会出现乱码的问题,比如汉字,用字节流读取的时候有可能因为一位字节没有读到就变成了乱码,字符流呢就完美解决了这个问题,字符流你们可以这样理解,字节流和编码表的组合就是字符流。因为有了编码表所以可以确定这个汉字有多少个字节,这样字节流就可以根据位数准确的读写汉字了。
3.字节流
字节流通常可以处理二进制数据,起始它可以处理任意类型数据,但不支持Unicode
1.InputStream字节输入流
InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取
package day8;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo04 {
public static void main(String[] args) throws IOException {
File f=new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
FileInputStream fis=new FileInputStream(f);//创建基于文件对象的字节输入流
byte []b= new byte[(int) f.length()]; //创建字节数组
fis.read(b);//以字节流的形式把文件读取到字节数组中
for (byte b1 : b) { //文件内容为 abc 对应的Ascll为97 98 99
System.out.println(b1);
}
fis.close();//用完后要关闭流
}
}
97
98
99
2.OutputStream字节输出流
OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo05 {
public static void main(String[] args) {
try {
File f=new File("C:/Users/AnGeng/Desktop/12.txt");// 创建文件对象
byte []b={65,66,67};//创建字节数组,这三个数对应的ASCLL码为ABC
//输入输出流可能会抛出异常,要么try catch 要么throws
FileOutputStream fos = new FileOutputStream(f);//创建基于文件对象的字节输入流
fos.write(b); //把字节数组写入
fos.close();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.字符流
1.FileReader字符输入流
FileReader 是Reader子类,以FileReader 为例进行文件读取
package day8;
import java.io.*;
public class Demo06 {
public static void main(String[] args){
try
{
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
FileReader fr = new FileReader(f);//创建基于文件对象的字符输入流 会报异常
char[] b = new char[(int) f.length()];
fr.read(b);//读取会报异常
for (char c : b) {
System.out.print(c); //读取数字和字母没问题,中文会有乱码
}
fr.close();
}catch(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2.FileWriter字符输出流
FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件
package day8;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo07 {
public static void main(String[] args) {
try {
File f = new File("C:/Users/AnGeng/Desktop/12.txt");// 创建文件对象
FileWriter fw=new FileWriter(f);//创建基于文件对象的字符输出流 会报异常
String s="123abc我爱小萝莉";
char []c=s.toCharArray();
fw.write(c);// 以字符流的形式把数据写入到文件中
fw.close();
}catch(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5.关闭流的方法
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。
1.在try中关闭
在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用
package day8;
import java.io.File;
import java.io.FileInputStream;
public class Demo09 {
public static void main(String[] args) {
try {
File f = new File("C:/Users/AnGeng/Desktop/12.txt");// 创建文件对象
FileInputStream fis = new FileInputStream(f);
// 在try 里关闭流 ,不安全,不推荐,打不开文件的话,那么流就不会被关闭
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.在finally中关闭
这是标准的关闭流的方式
- 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
- 在finally关闭之前,要先判断该引用是否为空
- 关闭的时候,需要再一次进行try catch处理
这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
package day8;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo09 {
public static void main(String[] args) {
File f = new File("C:/Users/AnGeng/Desktop/12.txt");// 创建文件对象
FileInputStream fis =null;
try {
fis=new FileInputStream(f);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(fis!=null)
try {
fis.close(); //在finally中关闭,虽然很麻烦,但是安全
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.使用try()的方式
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术
所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
package day8;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo09 {
public static void main(String[] args) {
File f = new File("C:/Users/AnGeng/Desktop/12.txt");// 创建文件对象
//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try ( FileInputStream fis = new FileInputStream(f);){
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.读取中文问题
1.编码
工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)
其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
Java采用的是Unicode
2.用FileInputStream 字节流正确读取中文
为了能够正确的读取中文内容
- 必须了解文本是以哪种编码方式保存字符的
- 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符
如本例,一个文件中的内容是字符中,编码方式是GBK,那么读出来的数据一定是D6D0。
再使用GBK编码方式识别D6D0,就能正确的得到字符中
package day8;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo09 {
public static void main(String[] args) {
File f = new File("C:/Users/AnGeng/Desktop/12345.txt");// 创建文件对象
try ( FileInputStream fis = new FileInputStream(f);){
byte []b=new byte[(int) f.length()];
fis.read(b);
for (byte b1 : b) {
int i = b1&0x000000ff; //只取16进制的后两位 就相当于全部转化为二进制之后再进行"与"运算
System.out.println(Integer.toHexString(i));//nteger类提供的一个将传入的int类型转成(无符号)16进制字符串的方法
}
System.out.println("把这个数字,放在GBK的棋盘上去:");
String str = new String(b,"GBK");
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
d6
d0
把这个数字,放在GBK的棋盘上去:
中
txt编码必须为ANSL 其他都不行!!!!!!!
3.用FileReader 字符流正确读取中文
FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了、
而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:
new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"));
在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。
idea 默认的编码为UTF-8,起始不用这么改也能读取中文
package day8;
import java.io.*;
import java.nio.charset.Charset;
public class Demo10 {
public static void main(String[] args) {
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
System.out.println("默认编码方式:"+ Charset.defaultCharset());
//FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
//而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
try (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 block
e.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 block
e.printStackTrace();
}
}
}
7.缓存流
以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲
缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
1.缓存流读取数据
缓存字符输入流 BufferedReader 可以一次读取一行数据
package day8;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class Demo11 {
public static void main(String[] args) {
/*
文件内容为:
abc123
我爱小萝莉
我喜欢御姐
*/
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
try (
FileReader fr=new FileReader(f); // 创建文件字符流
BufferedReader br=new BufferedReader(fr);// 缓存流必须建立在一个存在的流的基础上
){
while (true){
String line =br.readLine();// 一次读一行
if (line==null) break; //读到空行 就停止
System.out.println(line);
}
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
abc123
我爱小萝莉
我喜欢御姐
2.缓存流写出数据
PrintWriter 缓存字符输出流, 可以一次写出一行数据
package day8;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo12 {
public static void main(String[] args) {
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
try (FileWriter fw = new FileWriter(f);
PrintWriter pw =new PrintWriter(fw);
){
pw.println("我喜欢小萝莉");
pw.println("我喜欢御姐");
pw.println("我喜欢美少女");
// 会覆盖掉之前的内容
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.数据流
DataInputStream 数据输入流
DataOutputStream 数据输出流
使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。
注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。
package day8;
import java.io.*;
public class Demo13 {
public static void Write(){
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
try ( FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos=new DataOutputStream(fos);
){
dos.writeBoolean(true);//写如布尔值
dos.writeChar('2');//写入字符
dos.writeUTF("我爱小萝莉");//写入字符串
} catch (Exception e) {
e.printStackTrace();
}
}
public static void Read(){
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
char c =dis.readChar();
String str = dis.readUTF();
System.out.println("读取到布尔值:"+b);
System.out.println("读取到字符:"+c);
System.out.println("读取到字符串:"+str);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Write();
Read();
}
}
读取到布尔值:true
读取到字符:2
读取到字符串:我爱小萝莉
9.对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口
package day9;
import java.io.Serializable;
//把对象存进文件,必须这个类实现Serializable
public class Test implements Serializable {
public String s;
public int a;
}
package day9;
import java.io.*;
public class Demo01 {
public static void main(String[] args) {
//创建一个Test
//要把Test对象直接保存在文件上,务必让Test类实现Serializable接口
Test t=new Test();
t.a=16;
t.s="我喜欢萝莉";
File f = new File("C:/Users/AnGeng/Desktop/123.txt");// 创建文件对象
try(
//创建对象输出流
FileOutputStream fos=new FileOutputStream(f);
ObjectOutputStream oos=new ObjectOutputStream(fos);
//创建对象输入流
FileInputStream fis=new FileInputStream(f);
ObjectInputStream ois=new ObjectInputStream(fis);
){
oos.writeObject(t);//把对象写入文件
Test t2=(Test) ois.readObject();//把对象提取出来
System.out.println(t2.a);
System.out.println(t2.s);
}catch (Exception e){
e.printStackTrace();
}
}
}
16
我喜欢萝莉
10.System.in
package day9;
import java.io.IOException;
import java.io.InputStream;
// 敲入a,然后敲回车可以看到
// 97 10
// 97是a的ASCII码
// 10对应回车换行
public class Demo02 {
public static void main(String[] args) {
try(InputStream is = System.in;){
while (true){
int i=is.read();
System.out.println(i);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
ab
97
98
10
11.集合框架
1.ArrayList
1.定义
ArrayList是Java集合框架中的一个重要的类,它继承于AbstractList,实现了List接口,是一个长度可变的集合,提供了增删改查的功能。集合中允许null的存在。ArrayList类还是实现了RandomAccess接口,可以对元素进行快速访问。实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。和Vector不同的是,ArrayList不是线程安全的。
package day9;
import java.util.ArrayList;
public class Demo03 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
al.add(1);
al.add("萝莉");
System.out.println(al.size());
al.add(12.4);
System.out.println(al.size());
//只需要不断往容器里增加数据即可,不用担心会出现数组的边界问题。
//任何数据类型都行
System.out.println(al);
}
}
2
3
[1, 萝莉, 12.4]
2.常用方法
1.add 增加
package day9;
import java.util.ArrayList;
public class demo04 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
ArrayList al1=new ArrayList();
// 方法1,在后面添加
al.add("A1");
al.add("A2");
al.add("A3");
System.out.println(al);
//方法2:指定位置添加
al1.add("b1");
al1.add("b2");
al1.add(1,"b3");//在位置1添加元素
System.out.println(al1);
}
}
[A1, A2, A3]
[b1, b3, b2]
2.contains 判断是否存在
通过方法contains 判断一个对象是否在容器中
判断标准: 是否是同一个对象,而不是name是否相同
package day9;
import java.io.Serializable;
public class Test {
int i;
Test(int i) {
this.i = i;
}
public String s;
public int a;
@Override
public String toString() {
return "Test" + i;
}
}
package day9;
import java.util.ArrayList;
public class Demo05 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
for (int i = 0; i < 4; i++) {
al.add(new Test(i)); //存进去4个对象
}
//虽然添加的对象名称和参数和之前一致
//但是不是同一个对象
System.out.println(al.contains(new Test(2)));
}
}
false
3.get 获取指定位置的对象
通过get获取指定位置的对象,如果输入的下标越界,一样会报错
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
for (int i = 0; i < 4; i++) {
al.add(new Test(i)); //存进去4个对象
}
System.out.println(al.get(2));//获取2位置的对象
System.out.println(al.get(5));//下标越界,报错
}
}
day9.Test@1b6d3586
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 5, Size: 4
at java.util.ArrayList.rangeCheck(ArrayList.java:659)
at java.util.ArrayList.get(ArrayList.java:435)
at day9.Demo06.main(Demo06.java:12)
4.**indexOf **获取对象所处的位置
indexOf用于判断一个对象在ArrayList中所处的位置
与contains一样,判断标准是对象是否相同,而非对象的name值是否相等
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
System.out.println(al.indexOf(t2));
//虽然添加的对象名称和参数和之前一致
//但是不是同一个对象
System.out.println(al.indexOf(new Test(1)));
}
}
2
-1
5.remove 删除
remove用于把对象从ArrayList中删除
remove可以根据下标删除ArrayList的元素
也可以根据对象删除
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
System.out.println(al);
al.remove(2); //删除位置2的数据
System.out.println(al);
al.remove(t);//删除对象t
System.out.println(al);
}
}
[Test1, Test2, Test3]
[Test1, Test2]
[Test2]
6.set 替换
set用于替换指定位置的元素
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
System.out.println(al);
al.set(1,new Test(4));//在位置1替换
System.out.println(al);
}
}
[Test1, Test2, Test3]
[Test1, Test4, Test3]
7.size获取大小
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
System.out.println(al);
System.out.println(al.size());
}
}
[Test1, Test2, Test3]
3
8.toArray 转换为数组
toArray可以把一个ArrayList对象转换为数组。
需要注意的是,如果要转换为一个Test数组,那么需要传递一个Tes数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
//传递一个Tes数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组
Test []test =(Test[]) al.toArray(new Test[]{});
for (Test test1 : test) {
System.out.println(test1);
}
}
}
Test1
Test2
Test3
9.addAll 把另一个容器所有对象都加进来
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al=new ArrayList();
Test t=new Test(1);
Test t1=new Test(2);
Test t2=new Test(3);
al.add(t);
al.add(t1);
al.add(t2);
ArrayList al1=new ArrayList();
for (int i = 4; i < 8; i++) {
al1.add(new Test(i)); //存进去4个对象
}
al.addAll(al1);//把所有数据都加进来
System.out.println(al);
System.out.println(al1);
}
}
[Test1, Test2, Test3, Test4, Test5, Test6, Test7]
[Test4, Test5, Test6, Test7]
10.clear 清空
package day9;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
ArrayList al1=new ArrayList();
for (int i = 4; i < 8; i++) {
al1.add(new Test(i)); //存进去4个对象
}
System.out.println(al1);
al1.clear();//清空
System.out.println(al1);
System.out.println(al1.size());
}
}
[Test4, Test5, Test6, Test7]
[]
0
3.遍历
package day9;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo06 {
public static void main(String[] args) {
ArrayList<Test> al1=new ArrayList();
for (int i = 0; i < 5; i++) {
al1.add(new Test(i)); //存进去5个对象
}
//遍历方法1:增强for
for (Test test : al1) {
System.out.println(test);
}
System.out.println("__________");
//遍历方法2:普通for
for (int i = 0; i < al1.size(); i++) {
System.out.println(al1.get(i));
}
System.out.println("__________");
//遍历方法3:迭代器
Iterator<Test> iterator =al1.iterator();//获取迭代器
while(iterator.hasNext()){//hasNext()用于检测集合中是否还有元素。
System.out.println(iterator.next());//next()会返回迭代器的下一个元素,并且更新迭代器的状态。
}
}
}
Test0
Test1
Test2
Test3
Test4
__________
Test0
Test1
Test2
Test3
Test4
__________
Test0
Test1
Test2
Test3
Test4
2.其他集合
1.LinkedList
与 ArrayList一样,LinkedList也实现了List接口,诸如add,remove,contains等等方法,具体看上面
除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据
以下情况使用 ArrayList :
- 频繁访问列表中的某一个元素。
- 只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList :
-
你需要通过循环迭代来访问列表中的某些元素。
-
需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
package day9;
import java.util.LinkedList;
public class Demo07 {
public static void main(String[] args) {
LinkedList<Test> l=new LinkedList<Test>();
for (int i = 0; i < 3; i++) {
l.addLast(new Test(i)); //在尾部插入3个对象
}
System.out.println(l);
l.addFirst(new Test(4));//在首部插入
System.out.println(l);
System.out.println("查看首部数据"+l.getFirst());
System.out.println("查看尾部数据"+l.getLast());
System.out.println("查看不会导致数据被删除"+l);
l.removeFirst();//删除第一个
System.out.println("删除会导致后面的数据都往前移一个"+l);
System.out.println("查看第一个"+l.getFirst());
}
}
[Test0, Test1, Test2]
[Test4, Test0, Test1, Test2]
查看首部数据Test4
查看尾部数据Test2
查看不会导致数据被删除[Test4, Test0, Test1, Test2]
删除会导致后面的数据都往前移一个[Test0, Test1, Test2]
查看第一个Test0
2.二叉树的实现
假设通过二叉树对如下10个随机数进行排序
67,7,30,73,10,0,78,81,10,74
排序的第一个步骤是把数据插入到该二叉树中
插入基本逻辑是,小、相同的放左边,大的放右边
- 67 放在根节点
- 7 比 67小,放在67的左节点
- 30 比67 小,找到67的左节点7,30比7大,就放在7的右节点
- 73 比67大, 放在67的右节点
- 10 比 67小,找到67的左节点7,10比7大,找到7的右节点30,10比30小,放在30的左节点。
…
… - 10比67小,找到67的左节点7,10比7大,找到7的右节点30,10比30小,找到30的左节点10,10和10一样大,放在左边
package day10;
import java.util.ArrayList;
import java.util.List;
/*
二叉树:
每个节点和首节点进行比较,小的放左边,大的放右边
67,7,30,73,10,0,78,81,10,74
67为第一个节点,于是67为首节点
7比67小,放左边,左边节点没有定义,于是左节点定了一个node对象
这个节点内,7为value,也有左节点和有右节点,
然后传入第三个值 30,30和67比较,小,传入左边,左节点已经有了
左节点的value为7,然后7的左节点和右节点为空,于是,30和7比较
30比7大,所以7的右结点为30
73和67比较,大,于是放到67的右节点
10比67小,于是放到67的左节点
67的左节点为7,10和7比较,大
放到7的右节点,7的右节点为30,10和30比较
比30小,放到30的左节点,需要重新new 一个node对象
*/
public class Node {
public Node LeftNode; // 左子节点
public Node RightNode; // 右子节点
public Object value; //值
public void add(Object v){
if(null==value) value=v; // 如果当前节点没有值,就把数据放在当前节点上
// 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
else{ // 新增的值,比当前值小或者相同
if((Integer)v-((Integer) value)<=0){
if(null==LeftNode)
LeftNode=new Node();
LeftNode.add(v);
}
// 新增的值,比当前值大
else {
if(null==RightNode)
RightNode=new Node();
RightNode.add(v);
}
}
}
public List<Object> values() {
List<Object> values = new ArrayList<>();
// 左节点的遍历结果
if (null != LeftNode)
values.addAll(LeftNode.values());
// 当前节点
values.add(value);
// 右节点的遍历结果
if (null != RightNode)
values.addAll(RightNode.values());
return values;
}
public static void main(String[] args) {
int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
Node root=new Node();
for (int i : randoms) {
root.add(i);
}
System.out.println(root.values());
}
}
[0, 7, 10, 10, 30, 67, 73, 74, 78, 81]
通过上一个步骤的插入行为,实际上,数据就已经排好序了。 接下来要做的是看,把这些已经排好序的数据,遍历成我们常用的List或者数组的形式
二叉树的遍历分左序,中序,右序
左序即: 中间的数遍历后放在左边
中序即: 中间的数遍历后放在中间
右序即: 中间的数遍历后放在右边
如图所见,我们希望遍历后的结果是从小到大的,所以应该采用中序遍历
前序遍历A-B-D-F-G-H-I-E-C
中序遍历F-D-H-G-I-B-E-A-C
后序遍历F-H-I-G-D-E-B-C-A
前序(根左右),中序(左根右),后序(左右根)
3.HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
package day10;
import java.util.HashMap;
public class Demo01 {
public static void main(String[] args) {
//HashMap储存数据的方式是—— 键值对
HashMap<Integer,String> hm=new HashMap<Integer, String>();
hm.put(1,"萝莉");
hm.put(2,"小萝莉");
hm.put(3,"可爱萝莉");
hm.put(4,"可爱小萝莉");
System.out.println("键2的数据: "+hm.get(2));//把键传递过去
System.out.println("计算大小"+hm.size());//计算大小
hm.put(1,"御姐");//如果键值重复,那么新的会覆盖旧的
System.out.println("键1的数据: "+hm.get(1));
hm.remove(4);//删除
//遍历
for (int i = 1; i <=hm.size(); i++) {
System.out.println(hm.get(i));
}
}
}
键2的数据: 小萝莉
计算大小4
键1的数据: 御姐
御姐
小萝莉
可爱萝莉
4.HashSet
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
HashSet 实现了 Set 接口。
通过观察HashSet的源代码
可以发现HashSet自身并没有独立的实现,而是在里面封装了一个Map.
HashSet是作为Map的key而存在的
而value是一个命名为PRESENT的static的Object对象,因为是一个类属性,所以只会有一个。
HashSet 的操作基于set 删除,大小,清空,是否包含具体看上面!!!!!
package day10;
import java.util.HashSet;
import java.util.Iterator;
public class Demo02 {
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<>();
hs.add(1);
hs.add(23);
hs.add(77);
hs.add(67);
hs.add(67);//HashSet中数值不允许重复,重复的数值添加不进去
System.out.println(hs);//而且HashSet数据存储方式不是顺序存储
//遍历 迭代器
for(Iterator<Integer> it=hs.iterator();it.hasNext();){
System.out.println(it.next());
}
// 遍历 增强for
for (Integer h : hs) {
System.out.println(h);
}
}
}
[1, 67, 23, 77]
1
67
23
77
1
67
23
77
5.Collection和Collections
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
package day10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Demo03 {
public static void main(String[] args) {
//初始化集合
ArrayList<Integer> list=new ArrayList<>();
for (int i = 0; i < 10; i++) list.add(i); //添加元素
System.out.println(list);
Collections.reverse(list);//反转
System.out.println("反转后:"+list);
Collections.shuffle(list);//混淆
System.out.println("混淆后:"+list);
Collections.sort(list);//排序
System.out.println("排序后后:"+list);
Collections.swap(list ,0,1);//交换位置0和1的数据
System.out.println("交换后:"+list);
Collections.rotate(list,3);//向右滚动3个单位
System.out.println("滚动后:"+list);
}
}
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
反转后:[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
混淆后:[4, 5, 8, 6, 0, 1, 9, 7, 3, 2]
排序后后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
交换后:[1, 0, 2, 3, 4, 5, 6, 7, 8, 9]
滚动后:[7, 8, 9, 1, 0, 2, 3, 4, 5, 6]
12.泛型
package day10;
import java.util.ArrayList;
public class Demo04 {
public static void main(String[] args) {
ArrayList<Father>al=new ArrayList<>();//泛型,只能放Father类型的数据
//al.add("2");//放string,放不进去
al.add(new Father()); //放father的对象进去
al.add(new Son());//放放father的子类对象也可以
//通配符 表示这是一个Father 泛型或子类泛型
ArrayList<?extends Father> al1=al;
Father f=al1.get(1);//取出来,肯定能转化成father
//al1.add(new Father()); //但是不能放东西,因为能放father和它的子类
//那么种类太多了,提取时不知道怎么定义类型
//通配符 表示这是一个father泛型或者其父类泛型
ArrayList<? super Father> al2 = new ArrayList<Object>();
//? super Father 表示 al2的泛型是Father或者其父类泛型
//al2的泛型可以是Father
//al2的泛型可以是Object
al2.add(new Father()); //放father的对象进去
al2.add(new Son());//放father的子类对象也可以
Father f1=al2.get(1);//不能取东西 因为其泛型可能是Object,而Object是强转father会失败
Object o=al2.get(1);//但是object可以
//任意泛型通配符,
//泛型通配符? 代表任意泛型
//既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能
//所以只能以Object的形式取出来
//并且不能往里面放对象,因为不知道到底是一个什么泛型的容器
ArrayList<?> al3=al;
Object o1=al2.get(1);//只能以object类取出
al3.add(new Father());//不能放任何对象
/*
如果希望只取出,不插入,就使用? extends father
如果希望只插入,不取出,就使用? super father
如果希望,又能插入,又能取出,就不要用通配符?
*/
}
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a =scanner.nextInt(); //只可以输入整数
int b[]=new int[a];
for (int i = 0; i < a; i++) {
b[i] =Integer.parseInt(scanner.next());
}
for (int i = 0; i < a; i++) {
fun:for (int j = 0; j < i; j++) {
if (b[i]==b[j]){
b[i]=b[i]+1;
j=-1;
continue fun;
}
}
}
for (int i : b) {
System.out.print(i+" ");
}
}
}