第一阶段笔记

FUNDAMENTAL01

Java开发环境与搭建

一、Java

Java是一款高级编程语言 计算机只认识(0和1)

编程语言就是人类与计算机沟通的桥梁,Java只是编程语言中的一种,我们可以编程语言操作计算机。

编译运行过程(重点):

编译期:

.java源文件 -------编译 -----------.class字节码文件

运行期:

JVM加载并运行.class字节码文件(0和1)

名词:

JVM(Java Virtual Machine):Java虚拟机 ,主要就是用来加载运行.class字节码文件

JRE (Java Runtime Environment):Java运行环境,包含了JVM,还包含运行程序的环境

JRE = JVM + Java 系统类库工具

JDK(Java Development Kit):Java开发工具包,包含了JRE,还包含了Java开发所需的工具。

JDK = JRE +编译 ,运行等命令

如果是运行一个Java程序,至少需要JRE。

如果是开发一个Java程序, 至少需要JDK。


二、编译器工具

Eclipse(手动挡) 和 IDEA(自动挡)

IDEA 是JetBarins 公司 提供的 提供两个版本 : 社区版(免费) 终极版(收费)


开源:对用户开放实现细节

Java这款语言就是开源 , Eclispe (开源的) , IDEA社区版(开源的)


下载JDK 和IDEA的方式:

1.找李源老师...

2.doc.canglaoshi.org

Mac本的同学 下载安装完JDK以后 不需要配置环境变量。


开发步骤:

1.新建一个Java项目/工程 ----------------- 小区

2.新建一个Java的包 ---------------- XX号楼XX单元

3.新建一个Java的类 ----------------房子

Java中的注释分为三种:

单行注释:// 写上解释性文本

多行注释:/* 写上解释性文本 */

文档注释:/** 写上文档注释 */

Java中的编译方式分为三种:

Compile------------对类进行编译(不管类中内容是否修改过,一定会编译)

Rebuild ------------对项目进行编译(不管项目是否修改过,一定全部编译)

Make ------------默认的编译方式(只编译修改过的文件)

 

package day01;// 包名  day01   ---------- 具体楼+单元
public class HelloWorld { //类名 HelloWorld  --------房子
    //main功能的快捷键   main回车即可自动生成
    //main 功能 ----程序的主入口 :
    // 代码只能放在main的功能里面 才能运行
    //且代码是逐语句往下运行
    public static void main(String[] args) { //-------大门 入口
        /*
         * 1.严格区分大小写!
         * 2.写代码期间,所用到的符号 全部都要保证英文状态下输入,否则也会报错
         * 3.每句话如果写完 要用分号结尾
        * */
        //想输出一句话  显示在屏幕
        // 输出的功能  快捷键 sout 回车
        //文本性的内容:如果要输出 需要加双引号!
        System.out.println("Hello Java!!");//双引号中的内容所见即所得
        System.out.println(3);
        System.out.println(2);
        System.out.println(1);
    }//执行到这里 就代表 出口(程序结束)
}

day02变量

早期的计算机 ---------------------------------算数(数字的加减乘除)

现在的计算机 ---------------------------------算数(数字、文字、图片、音频、视频)

计算机处理的是数据!也就意味着离不开变量(就是存数的!)

变量:可以变化的量

变量的作用:存储数据

1.变量的声明 :开一个账户

 

 
int a;//声明一个int(整数)的变量 变量名为a
int b,c,d;//声明三个int(整数)的变量 变量名分别为 b  c  d
//int a; 编译错误 :因为已经声明过一个名为 a的变量了。不能重复声明

2.变量的初始化:给账户存钱

 

 
int e = 300;//声明了一个int(整数) 变量 名为e e里面存了300
int f;//声明一个int(整数) 变量名 为f
f = 150;//为f这个变量里面存 150

3.变量的使用:使用账户里面的钱

对变量的使用,本质使用的就是变量里存的内容!

使用变量之前一定要先声明+初始化!否则会报错!

 

 
int b = 5;//声明一个int(整数) 的变量名为 b  为b里面存了5
int c = b + 10;//声明一个int(整数)的变量名为c 里面存了 b + 10 的和,b是5 + 10 最后15存到变量c里面去
System.out.println("c");//c   -----因为双引号 所见即所得
System.out.println(c);//15  

测试代码

 

 
  //1. 变量的声明
//        int a;//声明一个int(整数)的变量 名为叫做 a
//        int b, c, d;//声明三个 int(整数)的变量 名分别为 b c d
        int a; 编译错误 :因为已经声明过一个名为 a的变量了。不能重复声明
//        //2.变量的初始化
//        int e = 300;//声明一个int(整数)的变量 名为e 里面存了300
//        int f;//声明
//        f = 100;
//        //3.变量的使用
//        int g = 5;//声明一个int(整数)的变量 名为g 存了5
//        int h = g + 10;//声明一个int(整数)的变量名为h 里面存了 g + 10 的和,g是5 + 10 最后15存到变量h里面去
//        System.out.println(h);//使用并输出h里存的15
//        int o = 10;
//        System.out.println(o); //10   使用变量前要声明+初始化
//        o = 20;
//        System.out.println(o);//使用变量 是以最后一次赋值的数据为准!
//
//        System.out.println("o");//打印输出的是o的字母 并不是用o这个变量!! 双引号的内容所见即所得
        // Ctrl + / 注释代码的快捷键

4.变量的命名规范

4.1 变量的命名 只能包含数字、字母、_和$ 注意不能以数字开头!、

4.2变量名是区分大小写的!

4.3变量名不能以关键字来进行命名!

4.4变量的命名要见名知意!

 

 
 //4.变量的命名规范
//        int 1a; 编译错误:变量名不能以数字开头
        int _01$;
        int $abc_;
        //Java严格区分大小写
        int a = 1;
        System.out.println(a);
//        System.out.println(A); 此A非彼 a
//        int void;  不能以关键字进行命名   关键字就是蓝色的字!
//        int static;
        //命名要见名知意!
        //建议大家下载有道词典  .....
        int score = 100;//用英文单词的见名知意
        //int 分数 = 100;//不能用 中文的见名知意  ------行业规范!
//        int fenshu = 100;//不能用 拼音的见名知意
        //变量的命名 遵循 小驼峰命名法 :小驼峰命名法 多个单词之间 首字母小写 后续第二单词首字母大写....第三....
        int myScore = 100;
        //类名 遵循 大驼峰命名法: 每个单词首字母大写.

day03:数据类型

数据类型的作用:决定了变量可以存储的类型 和 存储的范围。

数据类型分为:基本数据类型 和 引用数据类型(后面讲)。

常用的基本数据类型:

整数类型:int 、 long

小数类型:double

字符类型: char

布尔类型: boolean

不常用:byte (只是在IO相关内容会用到)。

short / float 保留c语言中的类型 工作用不到。


GB(吉字节)

1GB = 1024MB

MB(兆字节)

1MB = 1024kb

KB(千字节)

1kb = 1024b

b(字节)

1b = 8bit(位)


整数类型:

int: 在内存占用4个字节, 存储的范围 -21亿~21亿多(2147483647)!

1.整数的直接量默认为int类型,直接量是不能够超出当前类型所存储的范围,否则会报编译错误.

2.如果两个整数进行运算!结果是不会保留小数的!(会截断删除,不会四舍五入!)

3.整数如果运算时,若超出范围则会溢出,(溢出不是错误)但是需要避免,溢出的数据没有参考价值!

 

 
package day03;
/**
 * 数据类型的使用演示类:
 * 整数类型:
 *          int类型 占4个字节 可存储范围-2147483648  ~ 2147483647
 *         long类型
 * */
public class DateTypeDemo01 { //房子
    public static void main(String[] args) {//大门
              //整数直接量 默认为int类型
        int a = 100;//声明一个int类型的变量 变量名为a  存了100;
//        int b = 2147483648; 编译错误:整数直接量超过了int 这个类型可存储的范围
        int c = 5;
        int d = 2;
       // System.out.println(c / d );//2    (整数运算时不会保留小数位)
        //--------------------------------
        int number = 2147483647;//number已经存储的int 最大值
        int result = number + 3;
        System.out.println(result);//溢出的数据   -2147483646
        /**
         *  +1 -------------------(-2147483648)
         *  +1+1 -----------------(-2147483647)
         *  +1+1+1 ---------------(-2147483646)
         * */
    }
}

long:在内存中占用8个字节,可存储的范围 -900万万亿~900万万亿

1.长整数类型的直接量 需要在后面加上L

2.长整数类型在进行运算时,建议要在第一个数据后面加上L即可! 代表所有的结果都是long类型的.

 

 
          //长整数直接量
        long a = 10000000000L;//100亿  需要明确加L表示是long类型的直接量
                 // 10亿    * 2  * 10
        long b = 1000000000 * 2 * 10L;
        //System.out.println(b);//200亿
                //30亿 超出了int的范围
        long c = 1000000000 * 3 * 10L;
       // System.out.println(c);//溢出:-12949672960
        long d = 1000000000L * 3L * 10L;
        System.out.println(d);//300亿
变量占多大内存,取决的是数据类型  而并不是装载的数据!
int a = 5;
int b = 2147483647;

小数类型:

double(双倍精度):占8个字节 ,可以存储的数据很大很大很大的....

1.浮点型的直接量默认为double类型.

2.double类型的直接量 也可以明确表示加上后缀D

3.double类型的数据在进行运算时,可能存在误差! Math.round()四舍五入的功能

 

 
/**
 * 小数类型的使用演示类:
 * double 占8个字节
 */
public class DateTypeDemo02 {//房子
    public static void main(String[] args) { //大门
        //小数直接量 默认为double
        double a = 3.14;//声明一个double类型的变量 变量名为a  存了3.14
        double b = 3.0;
        double c = 2.9;
        //System.out.println(b - c);// 0.10000000000000009  可能存在误差
        double e = 6.0;
        double f = 4.9;
        //System.out.println(e - f);//1.0999999999999996   可能存在误差 无限接近
        double g = 6.0;
        double h = 1.9;
        System.out.println(g - h);//4.1   可能存在误差
        double r = Math.round(4.5);//可以四舍五入
        System.out.println(r);
    }
}

字符类型

char占用2个字节,表示的是字符的意思.

字符是什么?键盘上:字母/数字/汉字/特殊符号等...

Unicode:统一字符码,万国码.Unicode中划分了很多个子集,Java中char用的就是Unicode最常用的子集 ASCII码表.

1.一个字符对应一个码:表象是字符 本质是码(0~65535)之间.

2.字符类型的直接量,只能用单引号来进行存储,且只能存一个字符!

3.Java提供特殊符号,如果想要存储的话,需要转义(转换特殊含义) , 转义符号\

 

 
        char temp = 'a';//声明一个char类型的变量 名为temp 存了字符a
        System.out.println(temp);//a
        System.out.println((int)temp);//97
        char temp1 = '我';
        System.out.println(temp1);
//        char temp2 = 'ni'; 编译错误:char类型只能存单个值!
        char temp3 = '\'';//转义
        System.out.println(temp3);//   '
        char temp4 = '\\';//转义
        System.out.println(temp4);//   \

布尔类型

boolean占用内存1个字节,要么存true(真/成立) 要么存false(假/不成立).

 

 
        boolean a = true;
        System.out.println(a);//true
        boolean b = false;
        System.out.println(b);//false

类型转换

分为两种:

自动类型转换:将小的类型装载到大类型的过程.

从小到大顺序: byte --->short---->int ---->long---->float---->double

强制类型转换:将大的类型装载到小类型的过程.(强转有风险,使用须谨慎)

 

 
//自动类型转换:
//        int a = 100;//没有发生!
//        long b = a ;//发生自动类型转换
//        double c = b;//发生自动类型转换
        //强制类型转换:
        long a = 1000000000L;//10亿
        int b = (int)a;//使用强制语法:在需要强制转换的变量前面加上小括号 填上要强转的类型即可
        System.out.println(b);//10亿
        long c = 100000000000000000L;
        int d = (int)c;
        System.out.println(d);//一定会溢出!!
        double e = 3.1415926;
        int f = (int)e;//将小数强制转换为整数时  截断删除小数
        System.out.println(f);//3

规则:

1.整数直接量是可以直接赋值给 byte short char类型的!但是不能超过当前类型 的范围!

2.byte short char 如果进行运算时,系统会自动将其结果转换为int 类型。

3.小类型 与大类型进行运算时,系统会自动将类型和结果转换为大类型。

 

 
byte a = 1;
byte b = 2;
byte c = (byte) (a + b);//byte运算 其结果会自动转换为int类型
char d = '2'; //50
char e = '2'; //50
int f = d + e; //char运算时 本质上 运算的是字符对应的码
System.out.println(f);
char d1 = 50;//也可以存整数直接量的码
System.out.println(d1);//   ‘2’
byte b1 = 100;
int r = b1 + 20;//byte(小类型)  与 int(大类型) 运算 结果为   大类型

day04:运算符

1.数学运算符

符号:加(+) 减(-) 乘(*) 除( / ) 取余(%)

 

 
package day04;
/**
 * 数学运算符:
 * 自增自减运算符:
 */
public class OperationDemo01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 5;
        System.out.println(a + b);//15
        System.out.println(a - b);//5
        System.out.println(a * b);//50
        System.out.println(a / b);//2
                       //10 取余 5
        System.out.println(10 % 5);//10除以5 的余数为 0
        System.out.println(5 % 2);
        //---------------------------
        //取余操作时,若左边的数据小于右边的数据  其取余结果还是左边的数据
        System.out.println(1 % 3);//1
        System.out.println(2 % 3);//2
        System.out.println(3 % 3);//0
        System.out.println(8 % 15);//8
    }
}

2.自增自减运算符

自增:在自身基础上增加1 符号用: ++

自减:在自身基础上减少1 符号用: --

作用于变量的身上。

1. 单独运算时(只有当前变量被运算),符号在前在后都一样!

2.参与运算时(赋值也算运算),符号在前在后不一样!

符号如果在前,就先执行符号自增或自减,然后再参与其它运算。

符号如果在后,就会先参与其它运算,然后再进行自增或自减。

 

 
 //1.自增自减  单独运算时在前在后都一样。
//        int a = 5;
//        a--;
//        System.out.println(a);
        //2.参与运算时,自增自减在前在后不一样。
        /** 符号如果在前,就先执行符号自增或自减,然后再参与其它运算。
         符号如果在后,就会先参与其它运算,在下次使用变量时 则变为自增或自减的后的值
         * */
//        int a = 5;
//        int b = 5;
//        int c = a++;
//        int d = ++b;
//        System.out.println(a);
//        System.out.println(b);
//        System.out.println(c);
//        System.out.println(d);
//        int a = 5;
//        int b = 5;
//        int c = a--;
//        int d = --b;
//        System.out.println(a);
//        System.out.println(b);
//        System.out.println(c);
//        System.out.println(d);
        //
//        int a = 5;
//        int b = 5;
//        int c = --a - b--;
//        System.out.println(a);//4
//        System.out.println(b);//4
//        System.out.println(c);//-1
        //面试题:
//        int a = 1;
//        a = a++;  //相当于没有进行自增过  a值还是原来的数据
//        System.out.println(a);//1

3.关系运算符

关系运算符指的就是 大于( > ) 小于(<) 大于等于( >= ) 小于等于( <= )

等于( == ) 不等于(!=)

关系运算符的结果 一定是boolean类型,要么成立(true)要么不成立 (false)

 

 
int a = 50;   int b = 40;
boolean result =  a > b;                
a的数据 大于 b的数据吗?----------------true
int c = 50;  int d = 50;
boolean result1 = c >= d;
c的数据 大于等于 d的数据吗?------------true
    
int e = 100; int f = 99;
f++;
boolean result2 =  e == f;
e的数据 等于 f的数据吗?----------------true
        int a = 50;
        int b = 40;
        boolean result = a > b;
        System.out.println(result);//true
        System.out.println(a >= b);//true
        System.out.println(10 < 10);//false
        System.out.println(10 != 10);//false
        System.out.println( 5 == 4);//false

4.逻辑运算符

逻辑运算符的结果一定是boolean类型。

Java中逻辑运算符分为3种:

4.1:与(并且) 符号用:&&

 

 
逻辑与(并且)         现象:在逻辑与(并且)的关系中,两个或多个条件 有一个条件不成立,其结果一定不成立
                        一假俱假 ----- 见 false 即 false
能毕业吗?    
        条件一:课程学完了吗?    true       false     false    true       
         并且&&
        条件二:考试及格了吗?    false      true      false    true
                        
                    结果:    false      false     false    true

4.2:或(或者) 符号用: ||

 

 
逻辑或(或者)       现象:在逻辑或(或者)的关系中,两个或多个条件 有一个条件成立,其结果一定成立
                      一真俱真  ------ 见 true 即 true
能结账吗?
        条件一:微信有钱吗?    false     true      true      false       
          或者 ||
        条件二:带现金了吗?    true      false     true      false   
                    
                    结果:   true      true      true      false

4.3:非(取反) 符号用: !

 

 
逻辑非(取反)
        true: !true  非真即假
    
         结果:  false
             
        false: !false 非假即真
            
         结果:  true   

 

 
  //逻辑运算符
        int a = 5;
        int b = 10;
        int c = 5;
        boolean result = a > b && b > c;// fasle && true  ---->并且关系中见fasle
        System.out.println(result);//false      即是false
        System.out.println(a > b || b > c);//false || true --->或者关系中见true即true
        System.out.println(a == ++c && c >5);// fasle && true  ---->并且关系中见fasle即是false
        System.out.println(c);//6
        System.out.println(!true);//false    ----结果取反
        System.out.println(!false);//true    ----结果取反
 //判断年份是否为闰年案例:
        /**
         *  闰年的判定条件:
         *              条件一:年份能被4 整除   并且  年份不能被100整除
         *                 或者
         *              条件二:年份能被400 整除
         * */
        Scanner s = new Scanner(System.in);//创建一个扫描器
        System.out.println("请输入要判断的年份");
        int year = s.nextInt();//使用扫描器读取控制台输入的int数据的功能
        boolean r1 = year % 4 == 0 && year % 100 != 0;  //闰年条件一
        boolean r2 = year % 400 == 0;//闰年条件二
        boolean result = r1 || r2;//条件1 或 条件2 的结果 只要有一个成立 结果则成立
        System.out.println(result);//2000年是闰年
        //方式二:
//        boolean result1 = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
//        System.out.println(result1);

短路逻辑

 

 
短路与(并且):
          
           相亲成功的两个条件!
            1.相亲对象是女生       true        false
              并且
            2.勤俭持家            true
    
                    结果:       true        false
    短路与现象:如果前面条件不成立,程序则立刻返回不成立的结果,根本不去再执行判断后面的条件了。
短路或 (或者)
          相亲成功的两个条件!
          1.长得好看      true
            或者
          2.勤俭持家
    短路或现象:如果前面条件成立,程序立刻返回成立的结果,根本不去再执行判断后面的条件了。         
   int a = 5;
        int b = 20;
                          //发生短路与
//        System.out.println(a > b && ++b > a ); // false
//        System.out.println(b);//20
                         //发生短路或
        System.out.println( ++a < b || ++b >19 );//true
        System.out.println(b);//20
                        //未发生短路
        System.out.println(a < b && ++a > 19 );//false
        System.out.println(a);//7

5.赋值运算符

赋值号: = 赋值的顺序 从右往左赋值的过程。

扩展赋值号: += -= *= /= %=

在自身基础上快速进行四则运算后然后将运算结果赋值给自己

 

 
      //扩展赋值号的使用:
        int a = 10;
        a += 10;  // 等价于: a = a + 10;
        System.out.println(a);//20
        a -= 10;  // 等价于: a = a - 10;
        System.out.println(a);//10
        a *= 2;   // 等价于: a = a * 2;
        System.out.println(a);//20
        a /= 5;  // 等价于: a = a / 5;
        System.out.println(a);//4
        a %= 2;  // 等价于  a  = a % 2;
        System.out.println(a);//0

6.三元(三目)运算符

boolean ? 值1 :值2 ;

判断的逻辑 ? 若为true 返回值1 :若为false 返回值2;

 

 
       //三元表达式的写法
        int number1 = 10;
        int number2 = 20;
        //      判断的逻辑 ?  若为true 返回值1  :若为false 返回值2;
        int max = number1 > number2 ? number1 : number2;
        System.out.println(max);//最大值:20
        //使用三元表达式,用什么类型来接收结果 主要看值1 和值2的 类型
        int num = 10;
        boolean result = num % 2 == 0 ? true : false;
        System.out.println(result);//是偶数: true

7.字符串拼接符

字符是char类型 要用单引号存储1个字符!

字符串是String类型 要用双引号存储一串字符!

7.1 使用+号时,若两边都是数值类型 则会进行运算操作!

7.2 使用+号时,如果有一方 为字符串类型,则会进行拼接操作,变成一个字符串整体。

 

 
       int num = 10;
        boolean result = num % 2 == 0 ? true : false;
        System.out.println("num是否是偶数:" + result);//是偶数: true
        int age = 30;
        String name = "张三";
        System.out.println("我叫:"+name + "我的年龄是:"+age);
        System.out.println(10+10+""+30);//2030
        System.out.println(""+10+10+30);//101030
        System.out.println(10+10+30+"");//50
---------------------------------------------------------
        byte b = 5;
//        b = b + 5;//编译错误:小类型+大类型 系统会自动将结果转换为大类型
        b += 5;//使用扩展赋值 系统会自动进行强转 等价于  b = (byte)(b + 5);

day05:流程控制

Java中流程控制有三种结构,任何复杂的业务逻辑都可以通过这三种方式来实现。

1.顺序结构:代码逐语句 顺序执行,每句代码都走。

2.分支结构:有条件的执行某个语句,并非每句都走!

3.循环结构:有条件的重复执行某个语句,并没每句都走!

 

 
例如:
      购物满500 打8折  ---------购物满500 -------满足---打8折            单路分支       
        
      购物满500 打8折 不满打9折-------满500 打8折  ----不满足打9折         双路分支
    

分支结构:

单路分支:

 

 
package day05;
import java.util.Scanner;
/**
 * 单路分支结构的使用演示类:
 */
public class IFDemo {
    public static void main(String[] args) {
        /**  语法结构
         *          条件
         *      if(boolean){
         *          //条件成立执行的代码块
         *      }
         *      执行过程:
         *      判断if小括号中的boolean 值
         *      若为true 则执行条件成立执行的代码块
         *      若为false 代码跳过if的代码块... 继续往后执行
         * */
        Scanner s = new Scanner(System.in);
        System.out.println("请输入购物的金额..");
        double price = s.nextDouble();//金额 500
        if (price >= 500) { //如果金额 大于等于 500
            price *= 0.8; // price = price * 0.8;
            System.out.println("打完8折后应付金额:" + price);
        }
        System.out.println("已付款:" + price);
    }
}

双路分支:

 

 
package day05;
/**
 * 双路分支的使用演示类:
 */
public class IFElseDemo {
    public static void main(String[] args) {
        /** 双路分支的语法结构:
         *      if(boolean){
         *             //成立执行的代码块
         *      } else{ 否则
         *             // 不成立执行的代码块
         *      }
         *      执行过程:
         *      先去判断if小括号中的条件 是否成立
         *      若成立 执行 --成立执行的代码块
         *      若不成立 执行 --不成立执行的代码块
         * */
        //购物满500 打8折 不满打9折
        double price = 900;//金额
        if (price >= 500) {
            price *= 0.8;
            System.out.println("打完8折应付:" + price);
        } else { //否则
            price *= 0.9;
            System.out.println("打完9折应付:" + price);
        }
        System.out.println("已付款:" + price);
    }
}

多路分支:

 

 
package day05;
import java.util.Scanner;
/**
 * 多路分支的使用演示类:
 */
public class IFElseIFDemo {
    public static void main(String[] args) {
        /**多路分支的语法结构:
         *      if(条件1){
         *          //若条件1成立则执行的代码块
         *      } else if(条件2){
         *          //若条件2成立则执行的代码块
         *      }
         *      else if(条件3){
         *          //若条件3成立则执行的代码块
         *      }
         *      执行过程:
         *      先判断 条件1 若条件1满足 则执行条件1的代码块 否则
         *      再判断 条件2 若条件2满足 则执行条件2的代码块 否则
         *      再判断 条件3 若条件3满足 则执行条件3的代码块....
         * */
        /**
         * 需求: 根据用户输入的分数  来判断分数的等级。
         *      A. 分数 大于等于 90   输出优秀!
         *      B. 分数 小于90 并且 分数大于等于 80  输出良好!
         *      C. 分数 小于80 并且 分数大于等于 70  输出一般!
         *      D. 分数 小于70 并且 分数大于等于 60  输出及格!
         *      E. 分数小于 60   输出  不及格!
         * */
        Scanner s = new Scanner(System.in);
        int score = s.nextInt();//分数
        if (score >= 90) { //如果分数 大于等于 90
            System.out.println("优秀!");
        } else if (score >= 80) {//否则 如果分数大于等于80
            System.out.println("良好");
        } else if ( score >= 70) {//否则 如果分数大于等于70
            System.out.println("一般");
        } else if (score >= 60) {// 否则 如果分数大于等于60
            System.out.println("及格");
        } else {//否则 如果能执行到这里 说明上述条件没有一个 成立  则表示不及格
            System.out.println("不及格!");
        }
        System.out.println("程序继续往后执行.....");
        /**  练习: 通过控制台的方式 获取用户输入的年龄 根据年龄判断人生阶段
         *      int age = ?;
         *      A. 如果 age 大于等于 0 并且小于 5   输出幼年!
         *      B. 如果 age 大于等于 5 并且小于18   输出青年!
         *      C. 如果 age 大于等于 18 并且小于 35 输出成年!
         *      D. 如果 age 大于等于 35 并且小于 50 输出中年!
         *      E. 如果 age 大于等于 50 并且小于 65 输出中老年!
         *      F. 如果 age 大于等于 65 并且小于 150 输出老年!
         *      否则 年龄不合法!
         * */
    }
}

多路分支二:

switch...case 结构

适用性:对数值的数据进行匹配判断是否相等,不能使用逻辑运算和关系运算。JDK1.7版本以后支持对String类型 判断是否相等。

主要判断传递的内容与程序设计的值是否相等。

优点:如果只对单个整数数据判断是否匹配某个数值时,用switch case结构做分支效率更高。

break //退出switch 结构的代码块

default 代表默认的意思 上述case都没匹配上 则会走到default

 

 
      Scanner s = new Scanner(System.in);
        System.out.println("1 取款  2 存款 3 转账 请输入指令");
        int userInput = s.nextInt();//接收用户再控制台输入的指令
        //判断的变量类型 需要是整数类型(不包括long) 或 String类型
        switch (userInput){
            case 1:
                System.out.println("取款业务开始办理中...");
                break;//退出switch 结构的代码块
            case 2:
                System.out.println("存款业务开始办理中...");
                break;//退出switch 结构的代码块
            case 3:
                System.out.println("转账业务开始办理中...");
                break;
            default://默认..走到这里 代表上述没有一个匹配上。
                System.out.println("输入指令有误");
        }
        System.out.println("代码继续往后执行....");

循环结构:

循环:字面意思指的就是重复执行某个事情(代码),反反复复的过程,称之为循环!

Java中循环分为3中:1. while 2. do..while 3. for(常用)

循环的三要素:1. 循环变量的初始化 2.基于循环变量的条件 3.循环变量的改变(朝着目标改变)

 

 
跑三圈
初始圈数                    圈数为0
够三圈吗?    不够    跑一圈   圈数为1
够三圈吗?    不够    跑一圈   圈数为2
够三圈吗?    不够    跑一圈   圈数为3
够三圈吗?    够了    不跑了
    
      三要素:
            1. int count = 0; //循环变量初始值
            2. count < 3;     //基于循环变量的条件
            3. count++;       //循环变量的改变

while:

 

 
package day06;
/**
 * while 循环的使用演示类:
 * */
public class WhileDemo {
    public static void main(String[] args) {
          /**  while 循环语法结构:
           *    1.循环变量初始值
           *    while(2.boolean){
           *           //若条件成立 则循环的代码块
           *           //3.循环变量的自增
           *    }
           *    执行过程
           *    先去判断while小括号中的条件是否成立
           *    若成立 则执行循环的代码块
           *    再去判断while小括号中的条件是否成立
           *    若成立 则执行循环的代码块
           *    .....
           *    若不成立
           *        代码逐语句往后执行....
           * */
          //
        //循环的三要素:1. 循环变量的初始化  2.基于循环变量的条件  3.循环变量的改变
//        int count = 0;//1
//        while (count < 3){//2
//            System.out.println("行动是成功的阶梯!我们都是最棒的!!");
//            count++;
//        }
//        System.out.println("写一个很重要程序....");
        // 练习  使用while循环  让业寿同学 跑10圈
        int count = 1;//初始值
        while (count <= 10){//循环条件
            System.out.println("业寿同学跑第"+count+"圈了!" );
            count++;//循环变量自增
        }
        System.out.println("跑完了 歇歇.....");
    }
}

do..while:

 

 
package day06;
/**
 *  do ..while 循环的使用演示类:
 * */
public class DoWhileDemo {
    public static void main(String[] args) {
        /** do .. while 循环结构:
         * //循环的三要素:1. 循环变量的初始化  2.基于循环变量的条件  3.循环变量的改变
         *      1.循环变量的初始化
         *      do{
         *          //循环的代码块
         *          //3.
         *      }while(2.条件);
         *      执行过程:
         *      先去做do 的循环代码块 然后再判断while小括号中的条件
         *      若条件成立 则再次执行 do的循环代码块  再判断while小括号中的条件
         *       若条件成立 则再次执行 do的循环代码块  再判断while小括号中的条件
         *       ....
         *       若条件不成立 则代码代码继续往后执行...
         * */
        // 今天大家非常棒!我们继续保持 !奥里给!   X3
        int count = 0;//1
        do {
            System.out.println("今天大家非常棒!我们继续保持 !奥里给!");
            count++; //3.
        }while (count < 3); //2.
    }
}

随机数:

Java提供了可以获取随机数的功能:

Math.random(); 返回给一个double类型 从 0 ~ 1 区间的小数 可取到0 取不到1

 

 
//---------随机数的使用
        double r = Math.random();//返回给一个double类型 从 0 ~ 1 区间的小数 可取到0  取不到1
        //System.out.println(r);
        double r1 = Math.random() * 100;//返回给一个double类型 从 0 ~ 100 区间的小数 可取到0  取不到100
        //System.out.println(r1);
        int r2 = (int) (Math.random() * 100);//返回的随机数强制int 类型
       // System.out.println(r2);
        
        //  (int)(随机数  * (最大值 - 最小值 ) +最小值); //500 ~ 1500 的随机数
        int r3 = (int)(Math.random() * (1500 - 500 ) +500);
        System.out.println(r3);
 

 
   /**  猜大小的案例:
         *  运行产生一个随机数 ,提示用户在控制台中输入数据 来进行猜大小
         * */
        int random = (int) (Math.random() * 100);//随机生成0 ~ 100 的整数
        Scanner s = new Scanner(System.in);//创建一个扫描器
        int user;
        do {
            System.out.println("开始猜大小 区间0~99");//需要
            user = s.nextInt();//接收用户输入的猜的数据  需要
            if (user > random) { //如果用户输入的数据 大于 系统产生的数据
                System.out.println("猜大了");
            } else if (user < random) {//如果用户输入的数据 小于 系统产生的数据
                System.out.println("猜小了");
            }
        } while (user != random);//用户猜的数据 不等于 系统产生的数据
        //如果 代码能执行到这里 那么一定是猜对了!
        System.out.println("猜对了");
    }

while 和 do ...while 的区别:

相同点:当不明确循环次数的时候,但是已知循环条件,可以进行选择。

不同点:while 循环先去判断 循环条件 ,再决定是否循环!有可能一次循环都不做。

do..while循环 先去做一次循环,再决定是否循环! 至少做一次循环体


for(常用的循环)当已知循环次数 就使用 for

循环关键字

continue:作用:跳过当次循环 直接到达第 ③步 (直接进入下次的循环执行过程)

break:作用在循环体中,代表退出当前循环(即使循环没做完也不做了...)

 

 
package day06;
/**
 * for循环使用演示类:
 */
public class ForDemo {
    public static void main(String[] args) {
        /**   for 循环的语法结构  当已知循环次数 就使用 for
         *   循环的三要素:①循环变量的初始化  ② 基于循环变量的条件   ③ 循环变量的改变
         *                ①            ②       ③
         *      for(int count = 0; count < 3; count++){
         *           //循环的代码块 ④
         *      }
         *
         *      执行过程:
         *      先去执行  ①   ②   ④
         *      然后执行  ③   ②   ④ ------- ③   ②   ④------- ③   ②   ④
         *      直到 ② 不满足 循环则结束  代码继续往后执行了....
         * */
        //同学们继续加油!!  x3
        for (int count = 0; count < 3; count++) {
            //System.out.println("同学们加油666!");
        }
        //  continue  循环中可使用的关键字  作用:跳过当次循环 直接到达第 ③步 (直接进入下次的循环执行过程)
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) { //如果成立 则为偶数
                System.out.println(i + ":是偶数");
                continue;//跳过当次循环,直接执行下次循环过程
            }
            System.out.println(i + ":是奇数");
        }
        //---------break 关键字  作用在循环体中,代表退出当前循环(即使循环没做完也不做了...)
        for (int i = 1; i <= 10; i++) {
            if(i > 5){
                System.out.println("马润同学肚子疼...剩下圈数不能跑了..");
                break;//退出循环
            }
            System.out.println("马润同学跑第" + i + "圈了");
        }
        System.out.println("不跑了 让孩子歇歇吧...");
    }
}

for循环小括号中声明的变量,作用域只在当前for可用,其它地方视为看不见!

 

 
for(int i = 0; i < 3; i++){
    
}
for(int i = 0; i < 3; i++){ //并不冲突
    
}

for练习题:

当运行程序以后,随机出10道100以内的加法运算,然后让用户输入答案 ,然后再判断是否答对。若答对+10分。

做完10道题后,对分数等级进行判定

 

 
(1) 55 + 65 = ?
    请回答!    
    120
    答对了!
(2) 67 + 99 = ?
    请回答!
    166
    答对了
(3) 16 + 89 = ?
    请回答!
    110
    答错了!
(4).....
(10)  1 + 1 = ?
    请回答!
    2
    答对了!
 输出信息:共答对了多少道题、成绩判定为 ?? 
  //随机出 1道 加法题。
        // 出题    答题      判题
        int score = 0;//分数
        Scanner s = new Scanner(System.in);//创建一个扫描器
        for (int i = 1; i <= 10; i++) { //10次循环
            int n1 = (int) (Math.random() * 101);//随机0~100的整数
            int n2 = (int) (Math.random() * 101);//随机0~100
            System.out.println("(" + i + ")" + n1 + " + " + n2 + " =?");
            System.out.println("请答题:");
            int user = s.nextInt();//接收控制台输入的数据 接收给用户
            if (user == (n1 + n2)) { //如果用户输入的数据等于 系统出题的和
                System.out.println("答对了");
                score += 10; //自增
            } else {
                System.out.println("答错了");
            }
        }

双重for循环

for循环嵌套:for里套个for

99乘法表的实现:

外层for循环控制轮数 ,或者叫做行数

内层for循环控制次数 , 或者叫做列数

 

 
 // 双重for  九九乘法表
        for (int num = 1; num <= 9; num++) { //外层走一轮
            for (int i = 1; i <= num; i++) {//内层走多次 (具体看循环条件)
                // 删除ln 代表不换行                                   //tab键
                System.out.print(i + " * " + num + " = " + (i * num)+"\t");
            }
            System.out.println();//换行
        }

数组

定义:代表一组相同数据元素的集合,数组是一种数据类型 (引用数据类型)

1.数组的定义和初始化

2.数组的使用(访问)

3.数组名.length 代表数组的总长度 ,通常用于for遍历数组时的条件.

 

 
数组可以理解为家里的衣柜,橱柜,鞋柜。
    
-----------------------------
int a;//声明一个int类型的变量名为a         ---------------里面只能放整数
int[] b;//声明一个int类型的数组  数组名为b  ---------------里面可以放多个整数
double[] c;//声明一个double类型的数组 数组名为c -----------里面可以放多个小数
    
int score1 = 100;
int score2 = 99;
int score3 = 60;
   ....
int score174 = 88;       
  //1.数组的定义
        int[] arr;//定义: 声明一个int类型的数组  数组名为arr
        //初始化方式动态初始化写法:
        //未知数组中每个格子存的数据是多少,但是已知存储的数据个数
        int[] arr1 = new int[3];//声明一个int类型的数组 名字为arr1 在内存中开了3个格子 分别存了默认0
        //初始化方式静态初始化写法:
        //已知数组中每个格子的具体数据,已知存储的个数
        int[] arr2 = {100,60,20};//声明一个int类型的数组 名字为arr2 在内存中开了3个格子,分别存100,60,20
        //int[] arr3 = new int[]{100,60,20}; 写法2
        //2.数组的使用
        int[] arr4 = new int[3];//声明一个int类型的数组 名字为arr4 在内存中开了3个格子 分别存了默认0
        arr4[1] = 666;//访问数组下标为1的这块地 存了666
        System.out.println(arr4[0]);//访问arr4数组第一块内容
        System.out.println(arr4[1]);//访问arr4数组第二块内容
        System.out.println(arr4[2]);//访问arr4数组第三块内容
        //System.out.println(arr4[3]);//ArrayIndexOutOfBoundsException:访问数组下标越界的异常
 

 
  //-----------------------------------------
        int[] scores = new int[7];//创建数组 开了 5块地 数组名scores
        scores[0] = 100;//访问下标为0 的这块地 存了100
        scores[1] = 99;//访问下标为1 的这块地 存了99
        scores[2] = 88;
        scores[3] = 77;
        scores[4] = 66;
        scores[5] = 66;
        scores[6] = 0;
//        System.out.println(scores[0]);
//        System.out.println(scores[1]);
//        System.out.println(scores[2]);
//        System.out.println(scores[3]);
//        System.out.println(scores[4]);
        //循环5次                 //长度
        // 依次遍历数组每个元素 并打印出来
        for (int i = 0; i < scores.length; i++) {
            System.out.println(scores[i]);
        }
        /**  需求: 通过在控制台的方式 录入10学生成绩 存入数组中. int[] scores = new int[10];
         *      例如: 请输入第 1 名同学的成绩?
         *            100
         *            请输入第2 名同学的成绩?
         *            19
         *            请输入第3 名同学的成绩?
         *            ....
         *            请输入第10名同学的成绩?
         *            100
         *            录入完毕!
         *            输出10名同学的平均成绩 :  总成绩 / 总人数
         * */

方法

方法称之为函数,方法主要用来表示功能.

适用性:当有一些重复的性的逻辑代码时,我们可以用方法来进行复用,减少代码冗余.

方法语法: [访问修饰符] 返回值类型 方法的名称 ([参数列表]) { 方法的代码块 }

1.访问修饰符:控制当前方法的可见范围 (后期会详细讲)

2.返回值类型:没有返回值类型用 void 来表示没有返回值,如果有返回值的话 定义的时候要写上返回值类型 , 其次在方法体的最后一行代码 要写上return 关键字 代表返回 然后再后面写上要返回的数据,返回的数据要与返回值类型匹配.

3.方法名(遵循小驼峰命名法) 见命知意.

4.参数列表:又叫做形式参数,定义方法时占一个坑,供外部使用时传入具体的数据内容.

5.方法体:用来存放该方法的功能逻辑.

方法分类:1.无参无返回值 2.有参无返回值 3.无参有返回值 4.有参有返回值

1.无参无返回值:仅仅只是想优化重复性的代码 没有其他的逻辑.

 

 
package day07;
/**
 * 方法的使用演示:
 * 无参无返回值的写法
 */
public class MethodDemo {
    public static void main(String[] args) {
        //多次定义 多次使用
        //一旦发生修改  都要变!
//        System.out.println("直拳");
//        System.out.println("摆拳");
//        System.out.println("上勾拳");
//        System.out.println("侧踹");
//
//        System.out.println("直拳");
//        System.out.println("摆拳");
//        System.out.println("上勾拳");
//        System.out.println("侧踹");
//
//        System.out.println("直拳");
//        System.out.println("摆拳");
//        System.out.println("上勾拳");
//        System.out.println("侧踹");
        attack(); //调用方法 从哪里来回哪去
        System.out.println("程序继续往后执行");
    }
    //方法的语法:  没有返回值 方法的名称 () { 方法的代码块  }
    //定义了一个无参数无返回 名为attack 的方法
    //加static 是为了能在main中调用 否则main不可访问attack方法
    static void attack() { //定义方法
        System.out.println("直拳");
        System.out.println("摆拳");
        System.out.println("上勾拳");
        System.out.println("侧踹");
        System.out.println("下勾拳");
    }
}

2.有参无返回值

 

 
package day07;
/**
 * 有参数无返回值的方法使用演示:
 * */
public class MethodDemo02 {
    public static void main(String[] args) {
        //传递实际参数:具体的数据   注意,具体的数据要与该功能需要的类型匹配
        //注意:name: 是属于编译器的提示 不需要写!!
        sayHi("乾隆");//调用功能
    }
    //方法表示的是功能
    //做一个问好的功能
    //返回值类型 方法的名称 () { 方法的代码块  }
    //定义功能
    //目标是希望谁调用这个问好功能,可以自己选择到底问谁好!
    //可以给方法加        形式参数:定义功能时占一个坑 但是具体坑里面是什么,要看使用者传递什么数据
    //程序设计中的一个要求: 对修改关闭 就是设计好一个功能后尽量不要修改它了
   static void sayHi(String name){
        System.out.println(name + "同学你好!");
    }
}

3.无参有返回值

 

 
package day07;
import java.util.Scanner;
/**
 * 无参数有返回值的使用演示:
 */
public class MethodDemo03 {
    public static void main(String[] args) {
        //方法有返回值 才可以接收!!
        double pi = getPI();//调用方法
        System.out.println(pi);// 3.1415926
        //只有方法有返回值类型 才可以放到打印语句中! 也就是下面这种写法
        System.out.println(getPI());//调用getPI方法并将返回值打印出来
//        //用随机数的功能
//        double r = Math.random();//返回一个0~1的double类型的小数!
//        System.out.println(r);
    }
    //做一个功能 别人调用时 返回 PI
    static double getPI() {
        //3.1415926  ----PI
        //返回   数据的类型要与方法的返回值类型 匹配!
        return 3.1415926;
    }
}

4.有参有返回值

 

 
    //有参有返回值:就是当一个功能 需要外部传递数据 进行计算 然后需要计算结果返回给使用者的时候
    // 两个int的数据求和运算的功能
    //       形式参数:占两个坑  供调用者传递具体运算的两个数据
    static int sum(int a, int b) {
        int r = a + b;//求 a + b的和
        return r;
    }

方法的重载:

好处:当方法参数个数和方法参数类型发生变化时,我们在定义对应的功能时,可以不需要起不同的方法名称,可以共用一个方法名. 对于调用者而言,非常友好,只需要传递要计算的内容即可,不需要区分使用的是哪一个功能,系统会根据调用者传入的数据来自动匹配对应的功能实现.

重载的条件:当方法参数个数或者参数类型不一样时,则可构建成重载!我们可以共用一个方法名.

不能重载的条件:参数个数和参数类型都一样 则不可构建成重载.

 

 
package day07;
/**
 * 有参数有返回值的使用演示:
 */
public class MethodDemo04 {
    public static void main(String[] args) {
        /**  需求是多变的!
         *   小峰做程序:
         *    做一个 10 + 20 的 运算
         *    做一个 100 + 1000 的运算
         *    做一个程序 5000000+ 50000 的运算
         *    做一个 50 + 50 +100 的运算
         *    做一个 1.5 + 4.5 的运算
         *    使用方法来实现:
         * */  //从需求的角度:数据是多变的! 数据的个数  数据的类型
//        int a = 10;
//        int b = 20;
//        int r = a + b;
//        System.out.println(r);//30
//        int c = 100;
//        int d = 1000;
//        System.out.println(c+d);//1100
        //.....
        int result = sum(10, 20);//调用功能
       //----------重载的使用
        System.out.println( sum(100,1000));
        System.out.println( sum(10,10,10));
        System.out.println(sum(1.5,4.5));
        //println 打印功能实现了重载
        System.out.println(10);
        System.out.println(1.1);
        System.out.println("hello");
        System.out.println(true);
        System.out.println('a');
    }
    //有参有返回值:就是当一个功能 需要外部传递数据 进行计算 然后需要计算结果返回给使用者的时候
    // 两个int的数据求和运算的功能
    //       形式参数:占两个坑  供调用者传递具体运算的两个数据
    static int sum(int a, int b) {
        int r = a + b;//求 a + b的和
        return r;
    }
    static int sum(int a, int b, int c) {
        return a + b + c;
    }
    static double sum(double a, double b) {
        return a + b;
    }
}

方法强化:

 

 
package day07;
/**
 * 数组 与 方法 结合的案例:
 */
public class MethodDemo05 {
    public static void main(String[] args) {
        /**
         *  需求: 写一个程序,求出数组中最大值!
         //      运行打印出数组的最大值.
         * */
        int[] array = {100, 60, 30, 20, 1000};
        int max = getArrayMax(array);//调用功能 传入要求最大值的数组
        System.out.println(max);
        int[] arr1 = {10000,500,30000,40000};
        int max1 = getArrayMax(arr1);//调用功能 传入要求最大值的数组
        System.out.println(max1);
        //        //思路:假设数组第一个元素是最大值,然后存给max变量
//        //    在遍历的过程中 需要用max 与 后面的内容 依次作比较 (始终保持max是数组中最大值)
//        int max = array[0];//把第一个元素 存给了max
//        for (int i = 0; i < array.length; i++) {
//             if(max < array[i]){//判断max 是否小于当前数组的元素
//                max = array[i];//则将当前数组元素的数据 给max
//             }
//        }
//        System.out.println("数组中最大值为:"+max );
    }
    //写一个功能 , 通过外部传入一个数组 求出数组中最大值 并返回出去!
    //                      占一个坑 具体什么数组由调用者传入
    static int getArrayMax(int[] array) {
        int max = array[0];//把第一个元素 存给了max
        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {//判断max 是否小于当前数组的元素
                max = array[i];//则将当前数组元素的数据 给max
            }
        }
        return max;
    }
}

FUNDAMENTAL02

面向对象

OO:面向对象(Object Oriented)

OOA:面向对象的分析

OOD:面向对象的设计

OOAD:面向对象分析与设计(二合一 ,开发效率更高)

OOP:面向对象编程(重点)

面向对象决定了入职速度和薪资水平

高质量代码 等价于高薪

高质量代码:

复用性要好! 扩展性要好! 维护性要好! 可移植性要好

可读性要好! 健壮性要好! 效率要好!

学完面向对象

听不懂我在讲什么 ---------落课

晕晕乎乎,能理解意思-----正常

大概听懂了,但是让你设计一个内容设计不出来---------优秀!

面向对象的三大特征:

封装,继承,多态

1.对象和类

类:抽象不具体的事物,通常表示一类事物的类别, 在程序中类表示模板.

对象:具体的,真实存在的,可以看的到的.

分析项目中的对象:

一个战舰,一堆深水炸弹对象,一堆侦察潜艇,一堆水雷潜艇,一堆鱼雷潜艇,一堆水雷,一堆鱼雷

(ObserverSubmarine)侦察潜艇

举例:

os1 --------x , y , width , height ,spped

os2 ---------x , y , width , height ,spped

os3 ----------x , y , width , height ,spped

os4 ----------x , y , width , height ,spped

.....

os100 -------x , y , width , height ,spped

100个侦察对象,就要写100次对应的数据(代码冗余)

使用类来去解决:

类 对象

模具 月饼

1.如何创建类

类中放所有对象共有数据/属性 -----------------变量

类中放所有对象共有的行为/方法 --------------方法

 

 
class Person{//人类的模板
    //所有对象共有数据/属性
    int age;//年龄
    String name;//姓名
    char sex;//性别
    //所有对象共有的行为
    void eat(){ //吃的行为
        
    }
    void sleep(){//睡觉
        
    }
    void play(){//玩耍
        
    }
}
-----------------------------------------------
class  car{//车类的模板
    //共有数据
    String color;//颜色
    String type;//型号
    double price;//价格
    
    //共有的行为
    void run(){//前进的行为
     }
    void back(){//后退的行为
        
    }
    void stop(){ //刹车的行为
        
    }
}
-------------------------------------------------
class ObserverSubmarine{//侦察潜艇类
    //所有侦察潜艇对象共有的数据
    int x;
    int y;
    int width;//宽
    int height;//高
    int speed;//速度
    //共有的行为
    void step(){ //移动的行为
        
     }
}    

变量分为两种:

1.成员变量:声明在类的里面,方法的外的变量,叫做成员变量,作用域在整个类中!

2.局部变量:声明在方法里面的变量,都叫做局部变量,作用域在当前方法种!

 

 
package oo.day01;
/**
 * 学生类模板:  代表所有学生对象
 * 类中放所有学生对象共有的属性(变量)和行为(方法)
 * 模板中不要放main 功能
 */
public class Student {
    //共有的属性   成员变量作用域在整个类体中
    String name;//姓名
    int age;//年龄
    int stuID;//学号
    //共有的行为
    void study() {//学习的行为
        //int a = 0;方法声明的变量为局部变量 作用域只在当前方法中可见
        System.out.println(name + "在刻苦学习...");
    }
    void sayHi() {
        System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
    }
}

自定义的类也是一种数据类型(引用类型)

2.如何创建对象

 

 
数据类型  变量名    创建一个学生对象
Student  stu   = new Student();  //创建了一个学生对象 将对象放到stu这个变量里面
Student  zs    = new Student();  //创建了一个学生对象 将对象放到zs这个变量里面
Student  ls    = new Student();  //创建了一个学生对象 将对象放到ls这个变量里面

3.如何访问对象

1.对象可以打点调用出什么内容,取决于对象的模板中有什么。

2.哪个对象 打点 调用 属性 并赋值 那么就是给那个对象的数据进行赋值。

3.哪个对象 打点 调用 方法 那么调用的方法里面用到数据都是那个对象的!

 

 
package oo.day01;
/**
 *  学生模板的测试类:
 *                  用于创建学生类对象
 * */
public class StudentDemo {
    public static void main(String[] args) {
        //创建对象的语法
        Student zs = new Student();//创建一个学生对象 存到了 zs变量里面.  拿zs就相当于用这个具体对象!
        //zs 这个变量 能打点调用 出什么 取决去当前 对象的模板!
        //哪个对象  打点 调用 属性 并赋值 那么就是给那个对象存到数据
        //访问对象的语法
        zs.name = "张三"; //为zs这个对象的姓名赋值 为"张三"
        zs.age = 30; //为zs这个对象的年龄赋值 为30
        zs.stuID = 1001;//为zs这个对象的学号赋值 为1001
        //哪个对象  打点 调用 方法  那么调用的方法里面用到数据都是那个对象的!
        zs.study();//调用zs这个对象 的学习行为
        zs.sayHi();//调用zs这个对象 的问好行为
        //------------------------------------------
        Student  ls = new Student();//创建一个学生对象 赋值给 ls这个变量
        ls.name = "李四";
        ls.age  = 40;
        ls.stuID = 1002;
        ls.study();
        ls.sayHi();
    }
}

2.项目需求:

 

 
深海杀手项目分析:
1.识别对象:
          战舰,深水炸弹,侦察潜艇(银色),鱼雷潜艇(金黄),水雷潜艇(军绿),水雷(圆形),鱼雷(长条形)           
2.分配职责:
         战舰(玩家):发射深水炸弹。
         深水炸弹: 可以攻击(三种潜艇),若打到时:
                  1.深水炸弹和被打倒的潜艇要消失。
                  2.打到侦察潜艇加10分,打到鱼雷潜艇加40分。
                  3.打到水雷潜艇加1条命。
         鱼雷潜艇: 可以发射鱼雷攻击战舰,若打到时:
                  1.鱼雷要消失
                  2.战舰要减1条命(命数为0,则游戏结束)
         水雷潜艇:可以发射水雷攻击战舰,若打到时:
                  1.水雷要消失
                  2.战舰要减1条命(命数为0,则游戏结束)
         
3.建立交互......    
    

2.1在src这个包下,创建一个包 cn.tedu.submarine

 

 
战舰类:        -------------------------Battleship
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }
深水炸弹类:     -------------------------Bomb
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }
侦察潜艇类:     -------------------------ObserverSubmarine
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }
鱼雷潜艇类:      -------------------------TorpedoSubmarine
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }
水雷潜艇类:      -------------------------MineSubmarine
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }
水雷类:        -------------------------Mine
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }      
鱼雷类:        -------------------------Torpedo
                                        属性: x ,y , width ,height,speed
                                        行为: 移动的行为  void step (){ }  
 

 
package cn.tedu.submarine;
/**
 * 游戏窗口类:负责运行游戏
 *          存放项目运行相关的代码逻辑
 * */
public class GameWorld {
    Battleship ship;//声明一个战舰类型的变量
    Bomb bomb;//声明一个深水炸弹类型的变量
    ObserverSubmarine os;//声明一个侦察潜艇类型的变量
    TorpedoSubmarine ts;//声明一个鱼雷潜艇类型的变量
    MineSubmarine ms;//声明一个水雷潜艇类型的变量
    Torpedo t;//声明一个鱼雷类型的变量
    Mine m;//声明一个水雷潜艇类型的变量
    //用来测试的方法
    void action() {
        ship = new Battleship();//创建一个战舰类对象 存到ship这个变量里面
        ship.x = 120;
        ship.y = 250;
        ship.width = 100;
        ship.height = 200;
        ship.speed = 50;
        bomb = new Bomb();//创建一个深水炸弹对象 存到 bomb里面
        bomb.x = 104;
        bomb.y = 31;
        bomb.width = 100;
        bomb.height = 200;
        bomb.speed = 50;
        os = new ObserverSubmarine();//创建一个战舰类对象 存到ship这个变量里面
        os.x = 120;
        os.y = 250;
        os.width = 100;
        os.height = 200;
        os.speed = 50;
        ts = new TorpedoSubmarine();//创建一个深水炸弹对象 存到 bomb里面
        ts.x = 104;
        ts.y = 31;
        ts.width = 100;
        ts.height = 200;
        ts.speed = 50;
        ms = new MineSubmarine();//创建一个深水炸弹对象 存到 bomb里面
        ms.x = 104;
        ms.y = 31;
        ms.width = 100;
        ms.height = 200;
        ms.speed = 50;
    }
    public static void main(String[] args) {
        GameWorld gw = new GameWorld();
        gw.action();
    }
    /**
     *   1.为什么要将各类的变量声明在main的外面呢?
     *      答:在main方法中声明的变量作用域只在main中,后期当前类其它的地方也需要用到这些变量,
     *          所以应设计为成员变量。
     *   2.为什么要单独写一个action方法做测试?
     *      答:因为main是static修饰的方法,比较特殊,普通成员变量无法方法main中使用,所以需要单独
     *          定义普通的action方法,来进行测试。
     *   3.为什么要创建GameWorld类对象再调用action方法?
     *      答:因为main是static修饰的方法,比较特殊,普通方法是无法放到main中使用,所以我们可以通过
     *         创建GameWorld类对象 因为对象可以调用action方法。
     **/
}

问:再action方法中,创建对象并为对象赋值数据的操作特别麻烦,每个对象照目前来讲要赋值5次数据,多个对象就要操作赋值多次,代码也非常冗余。

解决:可以通过构造方法来来解决创建对象赋值的繁琐过程!

构造方法:

构造方法又称为构造器。

1.适用性:再创建对象时,可以快速实现为当前对象的属性赋值(初始化赋值)。

2.构造方法的语法: 类名(){ } (注意:没有返回值那一说,加了就报错)

3.作用:构造方法本质作用是创建类对象必要的语法,

Java规定:如果不给类模板写构造方法,系统会自动送一个无参数的构造方法。

特性:当前类模板,若被创建对象时,系统则会自动调用对应的构造方法。

4.构造方法也可以重载:只要满足构造方法名相同,参数个数或参数类型不同即可。

5.如果在一个类中写了构造方法,系统则不会在赠送默认的构造方法(无参数的构造方法)

 

 
package oo.day01;
/**
 * 学生类模板:  代表所有学生对象
 * 类中放所有学生对象共有的属性(变量)和行为(方法)
 * 模板中不要放main 功能
 */
public class Student {
    //共有的属性   成员变量作用域在整个类体中
    String name;//姓名
    int age;//年龄
    int stuID;//学号
    //有参数的构造方法
    Student(String name1,int age1,int stuID1) { //构造方法再类被创建对象时执行
        name = name1;
        age = age1;
        stuID = stuID1;
    }
    //构造方法语法: 类名(){}
    //无参数的构造方法
    Student(){
    }
    //共有的行为
    void study() {//学习的行为
        int a = 0;//方法声明的变量为局部变量 作用域只在当前方法中可见
        System.out.println(name + "在刻苦学习..."); //隐式写法:this.name  代表当前对象的name
    }
    void sayHi() {
        System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
    }
}

this关键字:

this指代的是当前对象,使用this关键可以区分成员变量 和 局部变量。

哪个对象打点调用方法,那么this指代的就是这个对象

Java规定 局部变量可以与成员变量名字一样。在使用变量时,遵循就近原则。

解决:可以用this关键字 明确表示 成员变量 避免与局部变量冲突的问题。

 

 
main{
    Student zs = new Student();
    Student ls = new Student();
    Student ww = new Student();
}
zs.sayHi();-----------------this 指代的就 zs 这个对象
ls.sayHi();-----------------this 指代的就 ls 这个对象
ww.sayHi();-----------------this 指代的就 ww 这个对象

默认值:引用类型的默认值都是null (String,数组,自定义类)

值类型中:整数默认值为0 , 小数默认为 0.0 boolean类型默认为 false


如果要拿一个null(空的)引用类型变量 打点访问内容,会报异常!NullPointerException空指针异常。

内存是JVM来进行分配划分: 栈区 、 堆区 、 方法区(后续)

基本数据类型的变量直接存数据,引用数据类型变量存的是对象地址。

1.栈区:用来存放局部变量的区域,局部变量指的就是方法中声明的变量。特性:方法执行完以后,方法中所声明的变量 则会被销毁。

2.堆区:用来存放对象的区域,对象指的是通过new关键字的语法创建的对象!特性:堆区的对象地址 若没有被引用的话,则会变为内存垃圾。

3.方法区:后续讲.....

GC(垃圾回收器):会不定时会去清理堆中存放的内存垃圾。

引用类型的内存图,具体看图示:

Student s = new Student();

数组的内存图:

1.基本数据类型数组

 

 
int[] array = new int[3];

2.引用数据类型数组

 

 
Student[] stus = new Student[3];

问题:当前项目7个类,若被创建对象时,需要依次打点调用对象的数据内容进行初始化赋值,非常麻烦

解决,为当前7个模板类添加构造方法:

 

 
战舰类构造方法:
 Battleship(){//当前这个类模板的无参数构造方法
        x = 270;
        y = 124;
        width  = 66; //图片的宽
        height = 26;//图片的高
        speed = 20;//每次移动多少
        life = 5;//游戏开始5条命
    }
深水炸弹类构造方法:
   Bomb(int x, int y) {//构造方法
        this.x = x;
        this.y = y;
        width = 9;
        height = 12;
        speed = 3;//移动速度
    }
鱼雷类的构造方法:
  Torpedo(int x, int y){
        this.x = x;
        this.y = y;
        width = 5;
        height = 18;
        speed = 2;
    }
水雷类的构造方法:
Mine(int x, int y){
        this.x = x;
        this.y = y;
        width = 11; //宽
        height = 11;//高
        speed = 2;//速度
    }
侦查潜艇类的构造方法:
   ObserverSubmarine(){
        width = 63;
        height = 19;
        x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
        y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
        speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
    }
水雷潜艇类的构造方法:
  MineSubmarine() {
        width = 63;
        height = 19;
        x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
        y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
        speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
    }
鱼雷潜艇类的构造方法:
 TorpedoSubmarine(){
        width = 64;
        height = 20;
        x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
        y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
        speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
    }

问题:除了战舰只有一个,其他类型都会存在多个对象,如果存储同一类型的多个对象.

解决:数组解决 在GameWorld类中修改如下:

 

 
    Battleship ship;//声明一个战舰类型的变量
    Bomb[] bombs;//声明一个深水炸弹类型数组的变量
    ObserverSubmarine[] os;//声明一个侦察潜艇类型数组的变量
    TorpedoSubmarine[] ts;//声明一个鱼雷潜艇类型数组的变量
    MineSubmarine[] ms;//声明一个水雷潜艇类型数组的变量
    Torpedo[] t;//声明一个鱼雷类型数组的变量
    Mine[] m;//声明一个水雷潜艇类型数组的变量

问题:7个 类模板中,存在共性重复的属性和行为,代码冗余!

解决:可以通过继承来优化!

继承:

生活中的继承: 继承财产,钱不同自己挣,继承过来也能花.

皇位继承: 继承江山,江山不用自己打,继承过来也能坐.

软件中的继承: 继承代码,代码不用自己写,继承也能用.

继承的适用性:当多个类之间存在一些共性属性和行为时,且它们在概念(is a)上达到一致,才可以使用继承来优化冗余的代码.

1.父类/超类: 存放所有子类共有的属性和行为

2.子类/派生类:存放自己特有的属性和行为

3.子类对象不仅可以访问自己的 内容,还可以访问父类中的内容(继承过来就是自己有)。

4.父类对象 只能访问自己的内容。

5.继承具有传递性,单一性(一个类只能继承一个父类)

继承的语法: extends

泛化:从多个类中提取冗余代码到父类的过程 称之为 泛化!

 

 
class Person{ //人类 --------代表  父类 / 超类
    String name; //姓名
    int age;//年龄
    char sex;//性别
//----------------------------
    void eat(){ } //吃
    void sleep(){ }//睡
}
class Student extends Person { //学生    子类 / 派生类
    //学生对象共有的数据
    int stuID;//学号
    //共有的行为
    void study(){ }//学习
}
class Teacher extends Person{ //老师类   子类 / 派生类
    double salary;//工资
    //行为
    void teach(){ } 
}
class Doctor extends Person{//医生     子类 / 派生类
    int level;//职称
    //
    void cut(){ } //手术
}     

继承传递性:

 

 
class 爷爷类{
    传家宝();
}
class 爸爸类 extends 爷爷类{
    传家宝();  
}
class 儿子类 extends 爸爸类{
    传家宝();  
}

解决:将7个类模板抽取共性的属性和行为到SeaObject类中,然后7个类通过继承来复用父类中提供的属性和行为.

7个类删掉x y width height speed 行为 step ,继承SeaObject类即可

 

 
package cn.tedu.submarine;
/**
 *  海洋对象类:
 *           所有海洋对象的父类
 *           存放所有子类共有的属性和行为
 * */
public class SeaObject {
    //共有的属性
    int x ;
    int y ;
    int width;
    int height;
    int speed;
    //共有的行为
    void step(){//移动的行为
        System.out.println("海洋对象在移动......");
    }
}

Java规定:

实现子类之前,父类默认有自己的无参构造方法,构造方法不可以继承,各的各的.

1.如果实现了继承,在创建子类对象时,子类构造方法会先去执行父类的构造方法,然后再执行子类的构造方法里的内容. 若没有明确为父类或者子类提供构造方法,系统则会调用默认提供的构造方法. 子类的构造方法中有隐式写法-----> super();

2.若父类写了有参数的构造方法,而没写无参的构造方法,那么子类若实现继承,子类则报错!需要手动明确的调用父类有参的构造方法.

注意事项:若明确写了子类的构造方法,调用父类的构造方法时需要放到第一行.

现象:再创建子类对象时,一定会执行父类的构造方法 ,然后在执行子类的构造方法内容.

 

 
package oo.day02;
/**
 * 父与子构造方法的测试类
 */
public class SuperDemo {
    public static void main(String[] args) {
        Boo b = new Boo();//创建Boo这个对象
    }
}
class Aoo { //父类
    //    Aoo() {
//        System.out.println("父类Aoo的无参构造方法");
//    }
    Aoo(int a) {
        System.out.println("a的值为:" + a);
    }
}
class Boo extends Aoo {//子类
    Boo() {//无参构造方法
        super(5);
        System.out.println("Boo的构造方法中的内容");
    }
}

this:代表当前类

super:代表父类

super.成员变量 ------------------------------访问父类的成员变量(应用率极低)

super.方法 -----------------------------------访问父类的方法(后续会讲)

super() ------------------------------------访问父类无参构造方法,如果括号写了参数,明确访问 父类有参数的构造方法.

问题:当前侦查潜艇/水雷潜艇/鱼雷潜艇 的构造方法重复 冗余.

解决:在创建子类对象时 会先执行父类的构造方法,我们可以将重复多份代码提取到父类构造方法中,达到代码的复用.

在SeaObject类中做一个为潜艇提供初始化的构造方法:

 

 
 /**
     * 此构造方法是专门为三种潜艇提供构造的方法
     * 因为潜艇宽高不同,所以宽高不能写死,做成形式参数,由具体的子类传递它的宽高数据
     * x y  speed 初始化的数据都是一样的 则写死即可.
     * */
    SeaObject(int width,int height){//构造方法
        this.width = width;
        this.height = height;
        x = -width;//x坐标负一个图片的宽度 表示在屏幕左侧外面.
        y = (int) (Math.random() * (479 - height - 150) + 150); //随机y坐标
        speed = (int) (Math.random() * (3 - 1) + 1);//随机速度1~3
    }

三种潜艇分别构造方法内调用父类的构造方法传递具体宽高即可:

 

 
水雷潜艇:
  MineSubmarine() {
      super(63,19);//调用父类的构造方法 传递当前潜艇的宽高即可
    }
侦查潜艇:
  OberverSubmarine() {
      super(63,19);//调用父类的构造方法 传递当前潜艇的宽高即可
    }
鱼雷潜艇:
   TorpedoSubmarine() {
      super(64,20);//调用父类的构造方法 传递当前潜艇的宽高即可
    }

剩下的4个类,战舰/深水炸弹/水雷/鱼雷 的构造方法 赋值的过程是重复的.所以 可以在父类中提供一个为当前这4个类初始化的构造方法.

 

 
 /** 因为赋值的过程是重复的.
     *  此构造方法是为战舰.深水炸弹.鱼雷和水雷提供的
     *  因为这4个类的具体数据都不同,所以做成形式参数,具体由当前类使用时提供
     * */
    SeaObject(int x,int y ,int width,int height,int speed){
        this.x = x;
        this.y = y;
        this.width  = width; //图片的宽
        this.height = height;//图片的高
        this.speed = speed;//每次移动多少
    }

4个类 的构造方法如下:

 

 
战舰类:
 Battleship(){//当前这个类模板的无参数构造方法
        super(270,124,66,26,20);
        life = 5;//游戏开始5条命
    }
深水炸弹类
    Bomb(int x, int y) {//构造方法
        super(x,y,9,12,3);
    }
鱼雷类:
    Torpedo(int x, int y){
        super(x,y,5,18,2);
    }
水雷类:
     Mine(int x, int y){
        super(x,y,11,11,2);
    }

继承中构造方法的强化:

 

 
学生类:
package oo.day02;
/**
 *  学生类模板
 *           存放自己特有的属性和行为
 * */
public class Student extends Person { //学生类继承了 人类
    //学生对象共有的数据
    int stuID;//学号
    Student(String name,int age,char sex,int stuID){
        super(name, age, sex);
        this.stuID = stuID;
    }
    //共有的行为
    void study(){//学习
    }
}
//------------------------------------------------
package oo.day02;
/**
 *  老师类
 *        存放自己特有的属性和行为
 * */
public class Teacher extends Person {//老师类继承人类
    double salary;//工资
    Teacher(String name,int age,char sex,int salary){
        super(name, age, sex);
        this.salary = salary;
    }
    //行为
    void teach(){ }
}
-------------------------------------------------
    package oo.day02;
/**
 * 医生类
 * */
public class Doctor extends Person { //医生类继承 人类
    int level;//职称
    Doctor(String name,int age,char sex,int level){
        super(name, age, sex);
        this.level = level;
    }
    //
    void cut(){ } //手术
}
-------------------Person类---------------------------
package oo.day02;
/**
 * 人类 ------代表学生类、老师类、医生类的 父类
 * 存放子类共有的属性和行为。
 */
public class Person {
    String name; //姓名
    int age;//年龄
    char sex;//性别
    Person(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    //----------------------------
    void eat() {
    } //吃
    void sleep() {
    }//睡
    void sayHi(){
        System.out.println("大家好,我叫"+name+"今年"+age+"性别是:"+sex);
    }
}
    

问题:在测试代码中,因为有三个数组,学生数组,老师数组,医生数组,那么再去遍历当前这数组中的信息时,需要写3个for循环.

解决:把3个数组变成1个数组.

向上造型:

1.声明父 new子 的语法就叫做向上造型.

2.变量能打点调用什么,取决于这个变量类型有什么!

3.父大 子小

向上造型的好处,可以用父类型来代表不同的子类型

 

 
class Animal{ //动物类
    
}
class Tiger extends Animal { //老虎类
        
}
main{
    类型            对象
    Animal a = new Tiger(); //老虎 是  动物   ---语义通 
    Animal a1 = new Animal();//动物 是 动物   ---语义通
    Tiger t  = new Tiger(); //老虎 是  老虎   ---语义通
    
    Triger t1 = new Animal();//动物 是 老虎   ---语义不通  代码会报错.
}
 

 
  // 父 Person  子  学生  老师   医生
        //声明父      new 子对象
        Person p = new Student("小明",10,'男',1001);
        //p能打点调用什么 取决于Person中有什么属性和行为
//        p.stuID; p是Person 父类型,只能访问自己内容
        Person p1 = new Teacher("小明",22,'男',122222);
        Person p2 = new Doctor("小明",44,'男',5);
        Person[] pp = new Person[3];//声明父类型数组
        //父类型数组 可以存放不同的子类型对象.
        pp[0] =  new Student("小明",10,'男',1001);
        pp[1] =  new Teacher("小s",22,'男',122222);
        pp[2] =  new Doctor("小t",44,'男',5);
        for (int i = 0; i < pp.length; i++) {
            //调用父方法   执行子对象
            // 编译期间 : 调用父类的sayHi功能
            // 运行期间 : sayHi方法用的数据 取决于 当前对象是谁!
            //pp[i].sayHi();
        }

问题:在GameWorld类中,由于三种潜艇用三个数组来表示,二种雷用两个数组来表示,后期在测试的时候,因为存在多个数组,for循环要写多次.

解决: 因为三种潜艇的行为是一样的,所以可以单独用一个父类型数组来表示三种潜艇.

因为二种雷的行为是一样的,所以可以单独用一个父类型数组来表示二种雷.

在GameWorld类上方:

 

 
    Battleship ship;//声明一个战舰类型的变量
    Bomb[] bombs;//声明一个深水炸弹类型数组的变量
    SeaObject[] submarines;//代表三种潜艇
    SeaObject[] thunders;//代表两种雷

问题: 运行测试后,当前打印输出的信息不明确!例如项目中测试 都是海洋对象在移动... 包括继承测试类:打印的信息 不明确,不知道是医生的还是老师还是学生的信息....

解决:可以使用重写来解决

重写(override):

1.定义:子类可以通过重写的方式,来解决父类中存在的方法子类不受用的情况!重写是发生在父子关系才可以.

2.若子类重写了父类的方法,在编译期调用的是父类的,运行后则执行的是子类重写后的那个方法.现象:调用父 执行子

3.遵循两同两小一大原则也可成为重写:

两同: 方法名相同, 参数列表要相同。

两小:子类在重写父类方法时,返回值类型要等于或小于父类中那个方法

子类在重写父类方法时,异常要等于或小于父类中的那个方法 (异常第二阶段)

一大: 子类在重写父类方法时,访问权限要等于或大于父类中那个方法。(后续会讲)

重写的条件:子类如果想要重写父类的不适用方法,首先在子类中要写一个跟父类的那个方法一模一样. 方法体的内容 可以自行根据逻辑去实现即可.

 

 
@Override
    void sayHi() { //重写语法
        System.out.println("我叫"+name+"我是一名学生,我的学号是"+stuID);
    }
//-----------
 @Override
    void sayHi() {
        System.out.println("我叫"+name+"我是一名老师,我的工资:"+salary);
    }
------------
     @Override
    void sayHi() {
        System.out.println("我叫"+name+"我是一名医生,我的职称:"+level);
    }

修改项目中7个子类 重写父类的step方法:

 

 
@Override
    void step() {
        System.out.println("战舰通过键盘左右运动");
    }
-------------------------------------------------
@Override
    void step() {
        System.out.println("深水炸弹Y向下运动");
    }
--------------------------------------------------
@Override
    void step() {
        System.out.println("水雷y向上运动..");
    }
-------------------------------------------------
@Override
    void step() {
        System.out.println("鱼雷y向上运动...");
    }
----------------------------------------------------
@Override
    void step() {
        System.out.println("水雷潜艇x向右运动");
    }
--------------------------------------------------
@Override
    void step() {
        System.out.println("侦察潜艇x向右运动");
    }
-----------------------------------------------------
@Override
    void step() {
        System.out.println("鱼雷潜艇x向右运动");
    }

小作业:画图

 

 
        Person[] pp = new Person[3];//声明父类型数组
        //父类型数组 可以存放不同的子类型对象.
        pp[0] =  new Student("小明",10,'男',1001);
        pp[1] =  new Teacher("小s",22,'男',122222);
        pp[2] =  new Doctor("小t",44,'男',5);

重写强化:

 

 
重写的情况分类:
             情况一、Boo子类只想吃西餐    ------------不需要重写
             情况二、Boo子类只想喝果汁    ------------需要重写
             情况三、Boo子类要吃西餐也要喝果汁 --------需要重写
    
class Aoo{//父类
    void eat(){
        System.out.println("吃西餐");
    }
}
class Boo extends Aoo{ //子类
     void eat(){
        super.eat();//调用父类的eat方法  情况三 
        System.out.println("喝果汁");
    }
}

重写和重载区别 --------------------面试题

重写(override/overrideing) :发生在父子关系中,方法名和参数列表要相同。

重载(overload/overloading) : 发生在同一个类中(继承过来也算自己的),方法名相同,参数列表要不同(个数或类型)。

 

 
class Aoo{ 父
    void show(){
        
    }
}
class Boo extends Aoo{ 子
      void show(){    ----------发生了重写
        
    }
}
----------------------------------------- 
    
class Coo{
    void show(){
        
    }
}   
class Doo extends Coo{
    void show(int a){  -------------发生了重载
        
    }
}

画窗口

界面绘制相关的功能,swing相关的内容....不需要大家掌握,后期工作不用。

1.在Gameworld类上方 导入 画框 和 地板

 

 
import javax.swing.JFrame;// 导入 画框功能
import javax.swing.JPanel;// 导入 底板功能

2.GameWorld类继承JPanel类

3.在GameWorld类写一个普通方法,实现绘制窗口的逻辑功能,

 

 
  //绘制界面的方法
    void paintWorld(){
        //1.创建一个画框
        JFrame frame = new JFrame();
        this.setFocusable(true);
        frame.add(this);//将当前类 添加到画框里
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口时释放程序
        frame.setSize(641 + 16,479 + 39);//画框的大小
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);//设置窗口可见
    }

4.在main中调用

 

 
 public static void main(String[] args) {
        GameWorld gw = new GameWorld();
        gw.paintWorld();//调用绘制窗口的功能
        gw.action();//调用了测试的方法
    }

package(包)

如果仅仅类名是唯一标识的话,很有可能类名发生冲突。

1000个类 想1000个类名,所以Java设计package

作用:避免类名的冲突

现象:同包中的类名是不能冲突的,不同包类名没有冲突。s

包名:包名纯小写。

银行项目上亿....

域名反写.项目名称.模块名.类名

import(导入)

1.当前要使用的类,在同一个包下,可以直接访问,不需要导入。

2.不同包的类,在使用的时候,需要通过import 关键字来进行导入(类的全包名),才可以用

如果用到的是Java所提供的功能,若不导入功能的全包名 则不能使用。

 

 
package oo.day02;
import oo.day01.Student;
import oo.day01.Test;//类的全包名
public class Demo {
    public static void main(String[] args) {
        //如果当前类要用到不同包下同名的文件时,需要明确写上类的全包名(这种情况少见)
        oo.day01.Student s = new oo.day01.Student();
        oo.day02.Student s2 = new oo.day02.Student("",1,'1',1);
    }
}

访问修饰符:

访问修饰符 可以修饰属性、方法、类(一般不管)。

用来隐藏一些内容.用来暴露一些内容。

属性封装:数据私有化,方法公开化。为了数据安全,方法内可以去加一些限制条件,避免不合法的情况发生。

1.private :私有的,访问权限最小,只能当前类内部可见。

2.public:公开的, 访问权限最大,其它位置都可以。

3.默认的:什么都不写,访问权限是本类和同包类都可以访问。

4.protected:保护的,访问权限在本类,同包子类,同包类,不同包的子类 ,不同的类不可以!

一般都在父子关系下,在父类中写的数据和行为会加上 protected权限

 

 
class Wife{ //老婆类
    private String name = "王小花"; //属性私有化
    private int age  = 22;     //属性私有化
    private char sex = '女';
    
    public void setAge(int age){ //通过方法公开化 可以让外部设置数据时 可以加一些要求
        if(age < 25 && age >18){//
             this.age = age;
        }
    }
    
    public String getName(){  //方法公开化 让外部获取本类私有的name的方法
        return name;
    }
}

demo01包下:

Aoo类

 

 
package oo.demo01;
/**
 * private:私有的,范围权限是在当前类中
 * public:公开的,任意地方都可以访问
 * 默认的: 本类或同包其它类 可以访问   -----行业不用 用的很少
 * protected:保护 本类 、 同包类/子类可以用  不同包子类可用    ---- 不同包类不可用
 * */
public class Aoo {
    private int a ;//私有的属性
    public  int b;//公开的属性
    int c; //默认级别的属性
    protected  int d;//保护级别的属性
    void fun3(){ //默认级别的方法
    }
    public void fun2(){ //公开的方法
        c = 10;//本类可用
        fun3();//本类可用
    }
    private void fun(){ //私有方法
        a = 10;//本类都可以用a
    }
}

Boo类:

 

 
package oo.demo01;
public class Boo  extends  Aoo{
     void test(){
        d = 10; //同包子类可以用
     }
}

TestDemo类:

 

 
package oo.demo01;
public class TestDemo {
    public static void main(String[] args) {
          Aoo tt = new Aoo();
//          tt.a = 0; 私有的属性 其它不可访问
//          tt.fun(); 私有的方法 其它不可访问
            tt.b = 10; //公开的同包的其它类可以访问
            tt.fun2();//公开的同包的其它类可以访问
            tt.c = 50; //默认的 同包的其它类可以访问
            tt.fun3(); //默认的 同包的其它类可以访问
            tt.d = 90;//保护级别  同包的其它类可以访问
    }
}

demo02包下:

Coo类

 

 
package oo.demo02;
import oo.demo01.Aoo;
public class Coo extends Aoo {
    void test2(){
        d = 50;//不同包的子类可以访问保护级别的内容
    }
}

Demo类:

 

 
package oo.demo02;
import oo.demo01.Aoo;
public class Demo {
    public static void main(String[] args) {
        Aoo pp = new Aoo();
        pp.b = 50;//公开的内容 不同包其他类 也可以访问
        pp.fun2();//公开的内容 不同包其他类 也可以访问
//        pp.c = 0;   默认级别 不同包其他类不可访问
//        pp.fun3();  默认级别 不同包其他类不可访问
//        pp.d  = 0; 保护级别 不同的其它类不可访问
    }
}

访问修饰符总结:

 

 
访问修饰符       类内部         同一包个中       非同包子类       非同包类
 public          √              √               √              √    
 protected       √              √               √              
 默认的           √              √
 private         √   


关键字

final:代表最终的意思。

1.修饰变量:变量不能够被二次修改!声明final的变量时,需要初始化。

final修饰变量时 要写在变量类型前方

2.修饰方法:用fianl修饰的方法,该方法是不允许被子类重写的!

final修饰方法时 要写在方法返回值前方

3.修饰类: 用final修饰的类,该类就不能被继承!

final修饰类时 要写在class 前面

 

 
package oo.day03;
/**
 * 测试final 的类
 */
public class FinalDemo {
    private int a;//声明int 的 成员变量 名为 a
    //    private final int c; 声明final的成员变量 需要声明时初始化
    private final int b = 100;//变量 b 里面赋值的是 100
    public void fun() {
        System.out.println(b);
//        b = 200;  编译错误:final修饰的变量不允许二次修改
        final int d;//声明final的局部变量 声明时可以不做初始化
        d = 150;//第一次赋值
//        d = 350;编译错误:final修饰的变量不允许二次修改
    }
}
//final class Aoo{ //父类  final 修饰的类不能被子类继承
//    final void action(){ //final修饰的方法 不能被子类重写
//    }
//}
//class Boo extends Aoo{ //子类
//
//}

static:静态

变量:

1.实例变量 : 属于对象的(有多个对象就有多少份),在堆中存储,通过对象名打点访问。

2.静态变量:属于类的(且只有一个),在方法区中存储,所有对象共享静态变量。

修饰变量:用static修饰的变量称之为静态变量,通过类名打点去访问。

用static 修饰时,写到变量类型的前方,

.class字节码文件加载到方法区中,且只会被加载一次。

 

 
JVM:分配 栈区   堆区   方法区
1.栈区:存放局部变量
2.堆区:存放对象的区域
3.方法区:用来加载.class字节码文件(类中方法和静态方法/静态变量)    
 

 
package oo.day03;
/**
 * 静态的使用演示类
 */
public class StaticDemo {
    public static void main(String[] args) {
        Coo c1 = new Coo();//创建Coo对象 存给了 c1
        c1.show();//显示ab值的功能
        Coo c2 = new Coo();//创建Coo对象 存给了 c2
        c2.show();//显示ab值的功能
        Coo c3 = new Coo();//创建Coo对象 存给了 c3
        c3.show();//显示ab值的功能
        //静态修饰变量 不需要创建对象进行访问 可以通过类名点的形式访问!
        System.out.println(Coo.b);
        Coo.b = 30;
    }
}
class Coo {
    int a;//实例变量  属于对象的
    static int b = 10; //静态变量   属于类的
    Coo() {
        a++;
        b++;
    }
    void show() {                   // 对象的a               类的 b
        System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
    }
}

静态适用性:

当有一份数据需要被某个类别下所有对象共享:

例如:当侦察潜艇类别下的所有侦察潜艇对象都需要进行图片显示,那么图片的话,我们可以做一份静态的图片类型.让所有侦察潜艇对象所共享这一张图片,节省内存。

QQ音乐: -------------孤勇者.mp3

去电影院看电影 ---------中国机长.mp4

修饰方法:

1.用static 修饰的方法叫做静态方法,属于类,通过类名来打点使用

2.存储在方法区中。

3.静态的方法中,没有this隐式的写法,所有也就是说为什么静态的方法无法使用实例的成员(实例的属性或普通方法)。

静态方法以后在写工具功能的时候,才会用到。主要目的就是方便使用者可以直接通过类名点的形式就可以访问。

 

 
class Coo {
    int a;//实例变量  属于对象的
    static int b = 10; //静态变量   属于类的
    static void method(){ //静态方法
        System.out.println(b);//隐式的写法 Coo.b
//        System.out.println(this.a); 静态的方法是没有this传递的,所以无法访问this相关成员
//         this.show(); 静态的方法是没有this传递的,所以无法访问this相关成员
    }
    Coo() {
        a++;
        b++;
        //this.show();可以访问 有隐式的this 传递
    }
    void show() {                   // 对象的a               类的 b
        System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
    }
}

静态代码块:

1.由static修饰的代码块,称之为静态代码块。

2.属于类,当类被加载时(当第一次用到某个类时,类且只加载一次),静态代码块则会被只执行一次。

3.如果类被创建对象时,那么优先调用静态代码块,再调用该类的构造方法

4.也可以直接通过访问类的静态成员 达到加载的方式。

 

 
package oo.day03;
/**
 * 静态代码块的使用演示
 */
public class StaticDemo02 {
    public static void main(String[] args) {
        //类只会被加载一次
//        Doo d1 = new Doo();//此时Doo类加载到方法区中
        System.out.println(Doo.a);//这种情况也是可以使用类的加载
    }
}
class Doo {
    public static int a = 10; //静态变量 属于类的
    Doo() {
        System.out.println("Doo的无参构造方法执行了");
    }
    static { //静态代码块 当本类被加载到方法区时 执行 且只会执行一次
        System.out.println("Doo的静态代码块执行了.....");
    }
}

思考?

项目中很多类别都需要用图片,例如:当侦察潜艇类别下的所有侦察潜艇对象都需要进行图片显示,那么图片的话,我们可以做一份静态的图片类型.让所有侦察潜艇对象所共享这一张图片,节省内存。

设计:项目中所需要用的图片,都需要进行加载和初始化,可以单独创建一个类 处理图片资源加载相关的一些内容.类的单一职责.

在项目包下创建一个类ImageResources

 

 
package cn.tedu.submarine;
import javax.swing.*;
/**
 * 图片资源加载类:
 *              负责加载和初始化项目中所需要的图片
 * */
public class ImageResources {
    //ImageIcon 用来存图片资源的类型
    public static ImageIcon battleShip;//用来存战舰图片的变量
    public static ImageIcon bomb;//用来存深水炸弹图片的变量
    public static ImageIcon gameover;//用来存储游戏结束图片的变量
    public static ImageIcon mine;//用来存储水雷图片的变量
    public static ImageIcon minesubm;//用来存储水雷潜艇图片的变量
    public static ImageIcon obsersubm;//用来存储侦查潜艇图片的变量
    public static ImageIcon sea;//用来存海洋背景图片的变量
    public static ImageIcon start;//用来存游戏开始图片的变量
    public static ImageIcon torpedo;//用来存鱼雷图片的变量
    public static ImageIcon torpedosubm;//用来存储鱼雷潜艇的变量
    static{//静态代码块 当类被加载时 执行 代码块中的内容。
        battleShip = new ImageIcon("img/battleship.png");
        bomb = new ImageIcon("img/bomb.png");
        gameover = new ImageIcon("img/gameover.png");
        mine = new ImageIcon("img/mine.png");
        minesubm = new ImageIcon("img/minesubm.png");
        obsersubm = new ImageIcon("img/obsersubm.png");
        sea = new ImageIcon("img/sea.png");
        start = new ImageIcon("img/start.png");
        torpedo = new ImageIcon("img/torpedo.png");
        torpedosubm = new ImageIcon("img/torpesubm.png");
    }
}

并在类中构建测试图片加载是否成功的代码 放到main中运行为8则代表加载成功

 

 
    public static void main(String[] args) {
        System.out.println(battleShip.getImageLoadStatus());
        System.out.println(bomb.getImageLoadStatus());
        System.out.println(gameover.getImageLoadStatus());
        System.out.println(mine.getImageLoadStatus());
        System.out.println(minesubm.getImageLoadStatus());
        System.out.println(obsersubm.getImageLoadStatus());
        System.out.println(sea.getImageLoadStatus());
        System.out.println(start.getImageLoadStatus());
        System.out.println(torpedo.getImageLoadStatus());
        System.out.println(torpedosubm.getImageLoadStatus());
    }

常量

用static final 修饰的变量称之为常量,应用率较高。

常量特点:结合static 和final的特性,通过类名访问,不能二次修改

常量命名时:纯大写。

何时用:例如有一份数据,不会变化 且经常使用。

 

 
package oo.day03;
/**
 * 常量的使用演示类:
 */
public class StaticFinalDemo {
    public static void main(String[] args) {
        //编译期间自动会将常量中的内容 转换为具体的值 效率更高
        // System.out.println(900);
        System.out.println(Eoo.C);//通过类名点的形式访问常量
//        Eoo.C = 1000; 不能二次修改
        
        //1.假设第一次用Eoo,先将Eoo加载到方法区
        //2.获取方法区中存的a这个静态的数据
        System.out.println(Eoo.a);//通过类名点的形式访问静态变量
    }
}
class Eoo{
    static int  a = 100;//静态变量  通过类名访问
    final int b = 100;//fianl修饰的变量 不能二次修改 且需要声明时初始化
    //常量
    static final int C = 700;
}

常量的适用性:因为我们的游戏窗口大小是固定的,而且后续其它地方都会用到这个窗口大小,如果一旦窗口大小发生修改 ,那么很多地方都需要改,所以我们可以将窗口大小设计为常量,方便使用和修改。

在GameWorld类上方 加上两个常量

 

 
   public static final int WIDTH = 641;//宽
    public static final int HEIGHT = 479;//高

在GameWorld类的paintWorld方法里 将使用到的宽高具体 数据修改为定义好的常量

在SeaObject类中为潜艇提供的构造方法 将使用的高度 也改成定义好的常量


总结设计规则:

1.将所有(概念统一的)类的共性属性和行为 提取到父类中 (泛化)

2.在父类中 若所有子类的行为具体实现逻辑都一样, 设计为普通方法。

若所有子类的行为具体实现逻辑各有不同,设计抽象方法。

3.接口(后续讲)

抽象方法:

1.用abstract修饰的方法 为抽象方法。

用abstract修饰方法时,写到方法返回值前即可

2.抽象方法必须存在于抽象类中

3.抽象方法不能有方法体

4.抽象方法是需要(必须)让子类重写实现的!

抽象类:

1.用abstract修饰类 为抽象类

2.抽象类中只是在原有的普通类基础上可以多放些抽象方法。(既可以放抽象也可以放普通方法)

3. 若一个类为抽象类,那么一定是需要由子类继承的!

4.子类必须实现重写抽象类中所有的抽象方法。

5.抽象类不能被创建对象的,但是可以创建抽象类型数组的对象。

6.抽象类的意义:

封装共有属性和行为 --------代码复用

为子类提供向上造型 --------调父执行子

可以包含抽象方法,由具体子类决定当前抽象方法的具体实现逻辑。

子类的行为逻辑虽然不同,但是我们在父类中写的那个抽象方法,在使用向上造型时 提供统一的入口。

 

 
//        SeaObject s = new SeaObject();//抽象类不能被创建对
          SeaObject[] ss = new SeaObject[5];//创建抽象类型的数组对象可以

疑问:

 

 
1).抽象方法还有意义吗?为什么不直接用普通方法呢?
   答:遵循面向对象设计规则,其次做成抽象方法的原因在于子类的行为逻辑都不一样,父类中做这个抽象方法就是为了约束子类必须要进行重写实现。
2).子类既然都重写了,那么父类的抽象方法为什么不能删?
   答:父类中存在抽象方法 就是为了实现向上造型后,父类可以提供一个入口,统一管理子类的行为,具体执行时 执行的则是各个不同子类的实现逻辑。 

画对象的图片只需要三个步骤:

1.对象的图片

2.绘制到窗口的x坐标

3.绘制到窗口的y坐标

贴切到项目中画对象

需要5个步骤

第一个步骤:

每个对象都需要绘制图片,意味着每个类都要写一个获取图片的方法。如果每个类都写获取图片的方法的话,则每个类都有共性的行为,此时可以提取到父类中,但是因为每个子类获取图片的具体行为逻辑不一样,所以提取到父类的获取图片的方法应该设计为抽象方法!

 

 
public abstract ImageIcon getImage();

第二个步骤:

获取图片的具体逻辑,每个对象都是有状态的,例如:活着的状态和死亡的状态,当判断当前对象活着的情况下再去返回图片。所有对象都有活着和死亡的状态,且这个状态是固定的。那么可以写到父类做两个常量表示死亡和活着的状态。还应该做一个表示当前对象状态的变量。

 

 
    public static final int LIVE = 0;//活着的状态
    public static final int DEAD = 1;//死亡的状态
               //当前状态
    public int currentState = LIVE;//默认初始化对象都是活着的状态

在SeaObject类中添加两个普通方法,用来判断是否是活着的或者是是否是死亡的方法。

 

 
  /** 判断当前调用方法这个对象 是否是 死亡状态 */
    public boolean isDead(){
        return currentState == DEAD;
    }
    /** 判断当前调用方法这个对象 是否是 活着状态 */
    public boolean isLive(){
        return currentState == LIVE;
    }

第三个步骤:

子类重写实现父类的getImage抽象方法

 

 
战舰类:
      //该方法被对象调用
    @Override//战舰比较特殊 并不是一打就死亡 所以在这里直接返回图片的就可以了
    public ImageIcon getImage() {
        return ImageResources.battleShip;//返回战舰图片
    }
-----------------------------------------------------------
深水炸弹类:
      @Override
    public ImageIcon getImage() {
        if (this.isLive()) {//如果当前这个对象是活着的
            return ImageResources.bomb;//返回深水炸弹图片
        }
        return  null;//如果能执行这一行 说明当前对象是死亡状态 返回null
    }
-------------------------------------------------------------
水雷类:
 @Override
    public ImageIcon getImage() {
        if(this.isLive()){//如果当前对象是活着的话
            return ImageResources.mine;//返回水雷图片
        }
        return  null;//执行到这里 则表示对象不是活着的 则返回null
    }
-----------------------------------------------------------------
水雷潜艇:
  @Override
    public ImageIcon getImage() {
        if(this.isLive()){//如果当前对象是活着的话
            return ImageResources.minesubm;//返回水雷潜艇图片
        }
        return  null;//执行到这里 则表示对象不是活着的 则返回null
    }
--------------------------------------------------------------------
侦察潜艇:
     @Override
    public ImageIcon getImage() {
        if(this.isLive()){//如果当前对象是活着的话
            return ImageResources.obsersubm;//返回侦察潜艇图片
        }
        return  null;//执行到这里 则表示对象不是活着的 则返回null
    }
----------------------------------------------------------------------
 鱼雷
    @Override
    public ImageIcon getImage() {
        if(this.isLive()){//如果当前对象是活着的话
            return ImageResources.torpedo;//返回鱼雷图片
        }
        return  null;//执行到这里 则表示对象不是活着的 则返回null
    }
------------------------------------------------------------------
鱼雷潜艇
      @Override
    public ImageIcon getImage() {
        if(this.isLive()){//如果当前对象是活着的话
            return ImageResources.torpedosubm;//返回鱼雷潜艇图片
        }
        return  null;//执行到这里 则表示对象不是活着的 则返回null
    }

第四个步骤:

获取对象图片的功能有了,但是每个对象都需要绘制啊,绘制的过程又重复了,将绘制图片的行为提取到父类中复用。

 

 
  /**
     * 因为每个子类都需要进行绘制,那么就将绘制的行为提取到父类中
     * 因为每个子类绘制的逻辑都是一样的 所以做一个普通方法
     * 参数需要一个画笔 通过外部调用方法时传递一个画笔
     */
    public void paintImage(Graphics g) {
        //如何表示调用该方法的对象
        if (this.getImage() != null) { //如果当前对象获取图片不为null
            ImageIcon icon = this.getImage();//获取当前对象图片
            //1.填null  2. 填 g   3.x坐标   4.y坐标
            icon.paintIcon(null, g, this.x+100, this.y);//绘制当前图片
                                        //+100是为了能看到潜艇 后续还是要删的
        }
    }

第五个步骤:

在GameWorld类中paint方法中测试绘制图片的功能:

 

 
 @Override               //g 可以理解为画笔
    public void paint(Graphics g) { //JPanle 提供了绘制图片的方法
        ship.paintImage(g);
        for (int i = 0; i < submarines.length; i++) {
            submarines[i].paintImage(g);
        }
    }

问题:运行游戏以后,潜艇能够绘制出来,但是不能移动. 潜艇的移动是自动发生的.

解决自动移动,需要先学

成员内部类(应用率不高)

类中套个类,外层的类称之为外部类,内层的类称之为内部类

1.内部类除了对外层类 以外不具备可见性

2.内部类对象 可以在外层类创建

3.内部类共享外部类的属性和行为 (包括私有)

4.内部类访问外部类 隐式写法 类名.this.xx

 

 
class Aoo{ //外部类
    
    class Boo{ //内部类
        
    }
} 
-----------------
    package oo.day04;
/**
 * 测试成员内部类
 * */
public class TestDemo {
    public static void main(String[] args) {
        Mama a = new Mama();
//        Baby b = new Baby(); 内部类对外不具备可见性
    }
}
class Mama{//外部类 --
    private int a;
    void action(){
        Baby b = new Baby();//外部类可以创建内部类对象
    }
    class Baby{//内部类
        int c;
        void test(){
            this.c = 100;//隐式this的传递 代表自己的东西
            Mama.this.a = 10;//内部类可以共享外部类的属性和行为 隐式写法类名.this.xx
            Mama.this.action();
        }
    }
}

匿名内部类(应用率高)

没有名字的内部类叫做匿名内部类.

适用性:如果一个子类,仅仅只是想重写实现父类中某个方法时,其他地方也根本用不到这个子类,那么我们可以直接使用匿名内部类的方式,快速实现重写的逻辑!

1.匿名内部类只会存在于子类要重写父类的(抽象/普通)方法时,才会用到.

2.匿名内部类 无法修改外部类的值!因为规定在匿名内部类的类体中使用外部类的属性时,会自动修饰为final

 

 
package oo.day04;
/**
 * 匿名内部类的测试类
 */
public class NsInnerClassDemo {
    public static void main(String[] args) {
        int b = 10;
        //使用匿名内部类的方式:
        //1.创建Aoo的子类 只不过没有名字
        //2.将当前这个没有子类对象 地址 赋值给了 a
        //3.花括号就是子类的类体!
        Aoo a = new Aoo() { //创建的匿名内部类的外部类是NsInnerClassDemo这个类
            //创建的匿名内部类的父类是 Aoo这个类
            @Override
            public void show() {
//                b = 100; 匿名内部类无法修改外部类的属性的
//                System.out.println(b); 匿名内部类使用外部类属性会默认为 final 
                System.out.println("匿名内部类重写Aoo的show方法");
            }
        };
        a.show();
        //常规的重写
//        Boo b = new Boo();
//        b.show();
    }
}
class Aoo { //-----作为父类
    public void show() {
        System.out.println("Aoo这个父类的show方法");
    }
}
//常规的重写
class Boo extends Aoo { //构建父子关系
    @Override
    public void show() {
        System.out.println("Boo子类重写父类的show方法");
    }
}

面试题:

问:内部类有独立的.class字节码文件吗?

有!

成员内部类:

外部类的类名 字节码文件 成员内部类 内部类的字节码文件

Mama Mama.class Mama.Baby Mama$Baby.class

内名内部类:

外部类的类名 字节码文件 匿名内部类的字节码文件

NsInnerClassDemo NsInnerClassDemo.class NsInnerClassDemo$1.class

动态入场

潜艇动态入场----------------- 自动发生的

水雷和鱼雷动态入场 --------- 自动发生的

如何自动发生:

1.定时器

2.线程 (第二阶段会讲)

定闹钟:

1.给闹钟定一个任务 2.开始延时多久的时间执行这个任务 3.闹钟任务执行一次后距离下 次执行的间隔时间.

定时器:

1.具体执行的任务 2.延时多久开始第一次执行 3.执行第一次以后下次执行的间隔时间

需要部署具体的任务,需要具体的定时器.

Java提供了任务类模板 TimeTask / 定时器模板 Timer

在GameWorld类上方导入

 

 
import java.util.TimerTask;   //任务模板
import java.util.Timer;      //定时器模板

在action方法中加上代码

 

 
   Timer timer = new Timer();//创建具体 定时器对象
         TimerTask task = new TimerTask(){
             @Override
             public void run() { //自定义的任务逻辑方法
                 System.out.println("叮叮叮~~~~~~~~~");
             }
         };
         //1.具体执行的任务  2.延时多久开始第一次执行(毫秒) 3.执行第一次以后下次执行的间隔时间(毫秒)
         timer.schedule(task,5000,1000);

潜艇(入场)生成是不确定数量的,没有办法去在运行之前定义死到底生成多少个潜艇,而是应该在游戏运行后,不断的产生。

数组扩容:数组扩容本质上是新创建了数组对象,并不是在原来的数组对象中修改,数组一旦开始确定了容量,大小不可变。

 

 
package oo.day04;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
 * 数组拷贝的方法:
 * 1.Arrays.copyOf   --------------------更多是基于源数组扩容或者缩容的情况下使用的。
 * 2.System.arraycopy -------------------更多是存在两个数组的情况下 例如将A数组的内容拷贝到B数组时。
 */
public class ArrayCopyDemo {
    public static void main(String[] args) {
//        //1.Arrays.copyOf
//        int[] array = {};
//        System.out.println("扩容前的长度" + array.length);//0
//        int a = 10;
//
//        /**
//         *  copyOf:
//         *         1. 填要扩容的数组
//         *         2. 扩容的长度
//         * */
//                //扩容以后会产生新的扩容后的数组对象 此时需要影响源数组.所以要赋值
//        array = Arrays.copyOf(array, array.length + 1);
//        array[0] = a;
//        System.out.println("扩容后的长度" + array.length);
//        System.out.println(array[0]);
        //-----------------------------
//        int[] arr2 = {5,4,70,50,3};
//        System.out.println("扩容前"+arr2.length);
//        for (int i = 0; i < arr2.length; i++) {
//            System.out.print(arr2[i]+"\t");
//        }
//        int b = 200;
//        arr2 = Arrays.copyOf(arr2,arr2.length+1);
//        //arr2.length-1 代表数组最后一个元素
//        arr2[arr2.length-1] = b;
//        System.out.println("扩容后");
//
//        for (int i = 0; i < arr2.length; i++) {
//            System.out.print(arr2[i]+"\t");
//        }
        //2.System.arraycopy
        int[] arr1 = {50,80,60,40,10};
        int[] arr2 = {1 ,1 ,1 ,1 ,1};
        /*
        * arraycopy
        *          1.拷贝的源数组
        *          2.从要拷贝的原数组哪个索引开始拷贝
        *          3.要拷贝到的目标数组
        *          4.从目标数组的哪个地方开始装
        *          5.拷贝过来的数量是多少 长度   (数组长度 - 从源数组拷贝的索引位置)
        * * */
        System.arraycopy(arr1,0,arr2,0,arr1.length);
        System.out.println("arr2完成拷贝");
        for (int i = 0; i < arr2.length; i++) {
            System.out.println(arr2[i]);
        }
    }
}

1.间隔1秒每次执行时生成潜艇对象,要在三种潜艇中,随机产生一个对象!

 

 
 /**
     * 生成潜艇的方法  返回值可以写具体潜艇类型吗?不可以!如果写了具体子类型
     * 方法则不通用
     * 返回值写父类型 SeaObject   实现父类型可以代表不同子类
     * */
    public SeaObject createSubmarine(){
        //产生 0~20的随机数
        int type = (int)(Math.random()*20);
        if(type <10){//如果生成的随机数在 0 ~ 9
            return  new ObserverSubmarine();//返回侦察潜艇
        } else if(type <15){//如果生成的随机数在 10 ~ 14
            return  new TorpedoSubmarine();//返回鱼雷潜艇
        }
        else {
            return new MineSubmarine();//返回水雷潜艇
        }
    }

2.随机产生的这个对象,要先将submarine数组扩容然后存储进去,放到run中调用

 

 
 /**
     * 潜艇入场的方法: 将随机产生的潜艇对象 装到  潜艇数组中
     */
    public void submarineEnterAction() {
        //1. 调用生成潜艇的方法 返回一个随机潜艇对象  接收
        SeaObject obj = createSubmarine();
        //2. 为潜艇数组在原有基础上扩1个容量
        submarines = Arrays.copyOf(submarines, submarines.length + 1);
        //3. 将对象 赋值给数组中下标最后一个空间
        submarines[submarines.length-1] = obj;
    }

3.paint方法在进行绘制时,submarine数组新增对象了,那么就会多绘制一个对象。

 

 
  //用来测试的方法
    void action() {
        Timer timer = new Timer();//创建具体 定时器对象
        TimerTask task = new TimerTask() {
            @Override
            public void run() { //自定义的任务逻辑方法
                submarineEnterAction();//调用潜艇入场的方法
                repaint();//刷新绘制 每0.01s 执行一次
            }
        };
        //1.具体执行的任务  2.延时多久开始第一次执行(毫秒) 3.执行第一次以后下次执行的间隔时间(毫秒)
        timer.schedule(task, 5000, 10);// 0.01s
    }

实现雷入场的功能

水雷和鱼雷分别由水雷潜艇和鱼雷潜艇,因为发射的逻辑基本一样.所以可以放到父类中写一个shootThunder方法,供潜艇来进行调用。

 

 
  /**
     * 生成雷的方法  返回值可以写具体雷类型吗?不可以!如果写了具体子类型
     * 方法则不通用
     * 返回值写父类型 SeaObject   实现父类型可以代表不同子类
     * 后续会被水雷潜艇或者鱼雷潜艇调用   侦察潜艇也可能会调用
     */
    public SeaObject shootThunder() {
        //创建雷对象之前  要先将雷对象的x 和y 坐标 算出来
        int x = this.x + this.width;//雷的x坐标
        int y = this.y - 5;//雷的y坐标
        //instanceof 关键字作用:判断 是否是某个类型的语法
        if(this instanceof MineSubmarine){//判断this这个对象 是不是 水雷潜艇
            return new Mine(x,y);//返回水雷
        }else if(this instanceof TorpedoSubmarine){//判断this这个对象 是不是 鱼雷潜艇
            return new Torpedo(x,y);//返回鱼雷
        }else {
            return  null; //如果能走到这一行 说明侦察潜艇调用了发射雷的方法 那么返回null
        }
    }

水雷和鱼雷也是自动入场的,所以在GameWorld类中写一个雷入场的方法,然后放到run中调用测试。

 

 
 private int thunderEnterIndex = 0;//控制雷产生的速度
    /**
     * 控制雷入场的方法 ---放到run中测试
     */
    public void thunderEnterAction() {//每0.01s执行
        thunderEnterIndex++;
        if (thunderEnterIndex % 100 == 0) {//控制每1000毫秒(1s)执行
//               1.for循环 遍历潜艇数组
            for (int i = 0; i < submarines.length; i++) {
              //2.在for循环体里面 访问潜艇数组的每个对象并调用shootThunder方法 声明一个SeaObject类型接收
                SeaObject obj = submarines[i].shootThunder();
                if (obj != null) {
//               3.为当前雷数组扩容
                    thunders = Arrays.copyOf(thunders, thunders.length + 1);
//               4.将接收的雷对象 装到雷数组中的最后一个元素中
                    thunders[thunders.length - 1] = obj;
                }
            }
        }
    }

所有的潜艇都是向右运动 ---------- x += speed;

所有的雷都是想上运动的 ---------- y -= speed;

深水炸弹是向下运动的 --------- y += speed;


当前所有的潜艇、雷、深水炸弹移动行为都是自动,在GameWorld类中写一个移动的方法,stepAction方法 里面遍历当前所有潜艇、雷、深水炸弹数组调用数组的每个对象的step方法. 在run中调用stepAction();

 

 
/**
     * 所有的潜艇、雷、深水炸弹的移动功能
     */
    public void stepAction() {
        for (int i = 0; i < submarines.length; i++) {
            submarines[i].step();//调用潜艇数组的每个对象的移动方法
        }
        for (int i = 0; i < thunders.length; i++) {
            thunders[i].step();//调用雷数组的每个对象的移动方法
        }
        for (int i = 0; i < bombs.length; i++) {
            bombs[i].step();//调用深水炸弹数组的每个对象的移动方法
        }
    }

深水炸弹通过按下键盘的空格键然后生成

深水炸弹的生成是基于一个事件 ----当玩家按下空格键

事件:发生了一件事情。 (在餐厅点了一份餐)

事件处理:发生事件以后要做的事情。 (点完餐以后让服务员通知你取餐)

侦听:用来检测要做事件处理的条件有没有达成。(服务员在点餐实时侦听有没有出餐)

Java中提供了键盘侦听相关的一些工具。

在GameWorld类上方导入两个包。

 

 
import java.awt.event.KeyAdapter; //键盘侦听器
import java.awt.event.KeyEvent;  //键盘事件

在action方法写上键盘侦听事件的逻辑代码

 

 
 //实现键盘侦听事件相关逻辑代码
        KeyAdapter adapter = new KeyAdapter() { //创建具体侦听对象
            @Override
            public void keyPressed(KeyEvent e) { //重写实现按下键盘键的事件
                //可以通过KeyEvent类直接访问键盘的按键
                //可以通过参数e里面提供一个方法 getKeyCode() 来获取用户当下按下的按键是什么
                if(e.getKeyCode() == KeyEvent.VK_SPACE){//判断用户按下的按键是不是空格键
                   //发射深水炸弹
                }
            }
        };
        this.addKeyListener(adapter);//将键盘侦听器添加到检测当中

在战舰类写一个发射深水炸弹的方法,shootBobm

 

 
/**
     *  当外部的战舰对象调用发射深水炸弹这个方法时,返回一个深水炸弹对象
     *  返回值 写深水炸弹类型
     * */
    public Bomb shootBomb(){
        return new Bomb(this.x,this.y);//返回深水炸弹对象 坐标位置是战舰的坐标
    }

在GameWord类中做一个深水炸弹入场的方法,并在判断是否按下是空格键的地方调用

 

 
/**
     * 深水炸弹入场的方法        放在当按下空格键的if内调用
     */
    public void bombEnterAction() {
        //1.通过战舰对象调用 shootBobm方法 并用深水炸弹类型来接收
        Bomb obj = ship.shootBomb();
        //2.为bombs数组扩1个容
        bombs = Arrays.copyOf(bombs,bombs.length+1);
        //3.将接收的对象赋值给bombs数组最后一个元素
        bombs[bombs.length-1] = obj;
    }

分析:所有的潜艇/雷/深水炸弹 当移出到屏幕外的时候,虽然看不见了,但是实际还存在内存中。

 

 
400毫秒   1个潜艇
1秒       2个潜艇  + 2个雷    共4个对象
1分钟     ------240对象
10分钟    ------2400个对象
-----------------------------------------
每10毫秒  移动的方法里要遍历2400个对象 paint方法里 也需要遍历2400个对象  ----------遍历4800个对象
    
1秒      ----处理......4800000个对象

优化内存:

内存泄漏:指的就是程序不断在创建对象。

内存溢出:指的就是内存没有可以在使用的空间了!(报异常错误)

垃圾回收器GC:处理的没有被引用的内存对象。

可以将已经移出的屏幕外的对象不要再放到我们对应的数组中。

在父类中写一个判断是否越界的方法 后续会被需要被判断是否越界的对象所调用的,也就意味着判断每个对象是否越界 此时方法返回值应该写boolean类型。

 

 
   /**
     * 是否越界的方法:
     *  为什么不做抽象方法而做普通方法:
     *                             因为三种潜艇的判断是否越界的标准是一样的,可以复用
     *                             其它三个类,深水炸弹,鱼雷,水雷类 不一样,则自行重写实现即可
     * */
    public boolean isoutBounds(){
            return this.x >= GameWorld.WIDTH;//判断潜艇是否超出屏幕的宽
    }
 

 
深水炸弹:
    
        @Override
    public boolean isoutBounds() {
        return this.y >= GameWorld.HEIGHT;//如果当前深水炸弹对象的y大于等于窗口的高
    }
水雷类:
   
    
     @Override
    public boolean isoutBounds() {
        return this.y <= 150 - this.height;//如果水雷的高小于等于水平面-水雷图的高
    }
鱼雷类:
    
    @Override
    public boolean isoutBounds() {
        return this.y <= -this.height;//如果当前鱼雷对象的y 小于等于 负自身图片的高度
    }

删除越界对象行为的方法,因为是自动发生的,所有在GameWorld类中写一个outOfBounds方法, 在run。

 

 
/**
     * 此方法是用来自动判断并删除越界对象的方法   放到run中调用
     */
    public void outOfBounds() {
        //1.遍历潜艇数组,并在循环里
        for (int i = 0; i < submarines.length; i++) {
            if (submarines[i].isoutBounds()) {//判断当前潜艇数组的每个对象是否越界
                //2.若成立则将当前对象剔除出数组中。
                submarines[i] = submarines[submarines.length - 1];//将数组最后的元素赋值给当前越界的元素空间 (替换)
                submarines = Arrays.copyOf(submarines, submarines.length - 1);//删除最后一个空间 (缩容)
            }
        }
        //遍历雷数组
        for (int i = 0; i < thunders.length; i++) {
            if(thunders[i].isoutBounds()){
                thunders[i] = thunders[thunders.length-1];
                thunders = Arrays.copyOf(thunders,thunders.length-1);
            }
        }
        //遍历深水炸弹数组
        for (int i = 0; i < bombs.length; i++) {
            if(bombs[i].isoutBounds()){
                bombs[i] = bombs[bombs.length-1];
                bombs = Arrays.copyOf(bombs,bombs.length-1);
            }
        }
    }

实现战舰的移动

通过按下键盘的(左键←) 和(右键→)实现左移和右移。

在战舰类中定义两个移动方法

 

 
  //左移
    public void leftMove(){
        this.x -= speed;
    }
    //右移
    public void rightMove(){
        this.x += speed;
    }

在GameWorld类中的键盘事件实现逻辑内

 

 
   //实现键盘侦听事件相关逻辑代码
        KeyAdapter adapter = new KeyAdapter() { //创建具体侦听对象
            @Override
            public void keyPressed(KeyEvent e) { //重写实现按下键盘键的事件
                //可以通过KeyEvent类直接访问键盘的按键
                //可以通过参数e里面提供一个方法 getKeyCode() 来获取用户当下按下的按键是什么
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {//判断用户按下的按键是不是空格键
                    bombEnterAction(); //发射深水炸弹
                }
                if(e.getKeyCode() == KeyEvent.VK_LEFT){//判断用户按下的按键是不是左移键
                    ship.leftMove();//调用战舰对象左移动
                }
                if(e.getKeyCode() == KeyEvent.VK_RIGHT){判断用户按下的按键是不是右移键
                    ship.rightMove();//调用战舰对象右移动
                }
            }
        };
        this.addKeyListener(adapter);//将键盘侦听器添加到检测当中

后续项目需求

当深水炸弹若打到潜艇分为两种不同的处理逻辑:

1.加分:

打到侦察潜艇 ,加10分

打到鱼雷潜艇, 加40分

2.加命:

打到水雷潜艇,加 1命


接口:

接口是一组行为的规范,接口不关心多个具体实现类之间是否是一种的关系,关注的它们的行为是否达到统一。

一旦实现(子)类 实现了 接口,那么必须要实现该接口中所有的行为。所以往往在设计时候,接口一般只放一个功能。(定义小而精的接口)

1.接口是一种数据类型(引用类型)

2.用interface 进行定义,代表接口。

3.接口中只能放常量 和抽象方法。接口中不管是常量还是抽象方法 默认访问权限都是公开的!

4.接口是不能被创建对象的。

5.接口是需要被实现类/子类 实现的。一旦实现(子)类 实现了 接口,那么必须要实现该接口中所有的行为。

6.一个类是可以实现多个接口的!实现时需要通过 implements 来实现,在后面写上接口名即可。

若一个子类又继承了一个父类 又想实现接口,子类 extends 父类名 implements 接口名

7.接口与接口之间 是可以继承的!例如:B接口 继承了 A接口,但是 C类 若实现了B接口,那么C同时还要实现A接口中的抽象内容。

 

 
class Aoo{ //定义一个 Aoo这个类
    
}
interface Boo{ //定一个 Boo 接口
    
}
 

 
package oo.day05;
/**
 * 接口的测试类:
 */
public class InterfaceDemo {
    public static void main(String[] args) {
//        Inter1 i1 = new Inter1(); 接口不能被创建对象
    }
}
interface Inter1{ //接口 中默认是常量
    //接口中不管是常量还是抽象方法  默认访问权限都是公开的!
    //    int NUM = 10; 默认是常量
    static final int NUM = 1;//标准常量写法
    // void test(); 默认是抽象方法
    abstract void test();
}
class  Aoo{ //父类
}
class Boo extends Aoo implements Inter1{// 先继承父类  再实现接口
    @Override
    public void test() {
    }
}

接口与抽象类的区别:

抽象类可以放构造方法; 接口没有构造方法。

抽象类可以放普通成员变量; 接口不可以。

抽象类可以放普通方法也放抽象方法;JDK1.8以前接口中只能放抽象方法。现在其实可以加静态的方法。

抽象类中的成员(变量或方法) 可以加访问修饰符; 接口中修饰符默认都是public。

接口不可以实现接口,但是可以继承接口。

一个类只可以继承一个类 ,但是可以实现多个接口。


当深水炸弹若打到潜艇分为两种不同的处理逻辑:

1.加分:

打到侦察潜艇 ,加10分

打到鱼雷潜艇, 加40分

2.加命:

打到水雷潜艇,加 1命

在项目包创建两个接口:EnemyScore (加分接口) EnemeyLife(加命接口)

 

 
package cn.tedu.submarine;
/**
 * 加分的接口 :主要提供加分的行为 供实现类去实现加分细节!
 * */
public interface EnemyScore {
    /**
     *  返回值类型 为 int   代表就是若实现类被打倒可以提供对应的分数
     * */
    int getScore();
}
-------------------------两个实现类:
    public class ObserverSubmarine extends SeaObject implements EnemyScore {
          @Override
       public int getScore() {
        return 10;
    }
  }
----------------
    public class TorpedoSubmarine extends SeaObject implements EnemyScore {
         @Override
     public int getScore() {
        return 40;
    }
 }

加命接口:

 

 
package cn.tedu.submarine;
/**
 * 加命的接口:主要提供加命的行为 供实现类去实现加命细节!
 * */
public interface EnemyLife {
    int getLife();//返回值为 命数 具体实现类决定返回多少命。
    
 }
------------------
    public class MineSubmarine extends SeaObject implements EnemyLife{
        @Override
        public int getLife() {
        return 1;
    }
    }

总结设计规则:

1.将所有(概念统一的)类的共性属性和行为 提取到父类中 (泛化)

2.在父类中 若所有子类的行为具体实现逻辑都一样, 设计为普通方法。

若所有子类的行为具体实现逻辑各有不同,设计抽象方法。

3.接口:(不同的类别或同样的类别)部分存在行为相同,我们可以使用接口来进行优化和复用。


错误的分类:

1.编译错误:编译期产生的错误----------全部都是语法错误 (欠练!)

2.运行异常:运行期产生的错误----------空指针异常/数组下标越界异常

3.程序不报错,呈现的现象跟你设计的现象不一样。

解决方法:

1.锁定错误:锁定可能出现问题的代码逻辑。

2.打桩:System.out.println(1);


FUNDAMENTAL03

面向对象三大特征:封装 、 继承 、多态

多态

人类在行为上多态:右侧睡 , 左侧睡,平躺睡,趴在睡

人类在个体上多态:有的人高,有的人矮 ,有的人胖,有的人瘦


程序上的多态 -----------------行为的多态,一个类型下的不同实现

引用类型变量能打点调出什么,要看变量的类型!

 

 
人 p = new 理发师();//向上造型   声明父 new 子
人 p1 = new 医生();
人 p2 = new 园丁();
p.cut();//p在调用的时候,用的人类型的cut ,但是具体运行执行p里面存的对象的cut行为
p1.cut();//p1在调用cut行为,是人类的. 但是具体执行是子对象的。
p2.cut();//p2 调用的是人类的行为,运行则是园丁的cut行为
abstract class 人{ 
    abstract void cut();//剪
}
class 理发师 extends 人 {
    void cut(){
        System.out.println("剪发");
    }   
}
class 医生 extends 人 {
    void cut(){
         System.out.println("做手术");
    }
}
class 园丁 extends 人{
    void cut(){
         System.out.println("剪草");
    }
}

当一个对象 被造型为不同类型时,具有不同的功能。

 

 
我 me = new 我();
me.授课();
me.孝顺父母();
me.互相卷();
--------------------------------
讲师 o1 = new 我();//向上造型
o1.授课();
父母的儿子  o2= new 我();
o2.孝顺父母();
其它老师的同事 o3 = new 我();
o3.互相卷();
interface 讲师{
    void 授课();
}
interface 父母的儿子{
    void 孝顺父母();
}
interface 其它老师的同事{
    void 互相卷();
}
class 我 implement 讲师, 父母的儿子,其它老师的同事{
    void 授课(){
        ...
    }
    void 孝顺父母(){
        ...
    }
    void 互相卷(){
        ...
    }
}

向上造型 / 自动类型转换

1.父大 子小 -------声明父 new 子

2.引用类型变量 能打点调出什么,取决于当前变量的类型。

3.能向上造型成功的 : 父 new 子 / 接口 new 实现类

向下转型 / 强制类型转换,可以成功的两个条件满足其一即可:

条件一:要强转的引用类型变量中存的引用 就是转换的这个类型

条件二:要强转的引用类型变量中存的引用 实现了要转换的接口类型,或继承了该类。

我们可以通过 instanceof 关键字来判断,判断当前对象是否符合要强转的类型

 

 
Aoo  o = new Boo();//向上造型
Boo o1 = (Boo)o;//可以完成强制转换  :符合强转成功的条件一
Inter1 i1 = (Inter1)o;//可以完成强制转换: 符合强转成功的条件二
Coo c1 = (Coo)o;//强制转换失败的异常:classcastException
interface Inter1{
    
}
class Aoo{//父类
    
}
class Boo extends Aoo implements Inter1{
    
}
class Coo extends Aoo{
    
}
 

 
package oo.day05;
/**
 * 类型转换测试 Demo
 * */
public class ClassCastDemo {
    public static void main(String[] args) {
        Aoo o = new Boo();//声明父new子  o中存的引用是Boo这个类型对象。
        Boo o1 = (Boo)o;//符合条件一:要强转的变量引用 就是这个类型
        if(o instanceof InterA){ //判断o对象是否可以转换为 InterA 接口类型
            System.out.println("o可以强转InterA接口类型");
            InterA i1 = (InterA)o;//符合条件二:要强转的变量引用 实现了该接口类型 或继承了该类
        }
        if(o instanceof Coo){//判断o对象是否可以转换为 Coo类型
            System.out.println("o是Coo这个类型");
            Coo c1 = (Coo)o;
        }
        Coo c1 = (Coo)o;//ClassCastException类型转换异常
//        Aoo o = new Boo();
//        Coo c1 = (Coo)o;
    }
}
interface InterA{
}
class Aoo{
}
class Boo extends Aoo implements InterA{
}
class Coo extends Aoo{
}

深水炸弹与潜艇的碰撞:

雷与 战舰的碰撞:

因为子类都碰撞检测的行为,提到父类中,且碰撞检测的逻辑都一样,在父类中做一个普通方法。

 

 
/*
* 检测碰撞的方法,这个方法由子类对象来进行调用。
*/
public boolean isHit(SeaObject other) {
        //this
        //other
        /**
         x1:   this的x  - other的宽
         x2:   this的x +   this的宽
         y1:   this的y -  other的高
         y2:   this的y +  this的高
         */
        int x1 = this.x - other.width;
        int x2 = this.x + this.width;
        int y1 = this.y - other.height;
        int y2 = this.y + this.height;
        int x = other.x;
        int y = other.y;
        //如果传入对象的x 在 x1和x2之间 y在y1和y2之间 则返回为true 代表撞上了。
        return  (x >= x1 && x <= x2) && (y >= y1 && y <= y2);
    }
-----------------------------------------
1)深水炸弹.isHit(潜艇对象); -------------------this指代的是深水炸弹 other指代的是潜艇对象
2)潜艇对象.isHit(深水炸弹); -------------------this指代的是潜艇对象 other指代的是深水炸弹
3)雷.isHit(战舰);         -------------------this指代的是雷对象  ohter指代的是战舰对象
4)战舰.isHit(雷);         -------------------this指代的是战舰对象  ohter指代的是雷对象
    

深水炸弹与潜艇的碰撞检测是自动发生的,需要在GameWorld类中写一个方法

bombBangAction,在该方法内部实现逻辑,遍历深水炸弹数组和潜艇数组,让两个数组中的每个对象进行检测。

 

 
  /**
     * 深水炸弹与潜艇碰撞检测的具体使用。  放run中调用。
     */
    public void bombBangAction() {
        for (int i = 0; i < bombs.length; i++) {//控制轮数
            Bomb b = bombs[i];//获取当前深水炸弹对象
            for (int j = 0; j < submarines.length; j++) {//次数
                SeaObject s = submarines[j];//获取当前潜艇数组中的对象
                if(b.isHit(s)){//当前深水炸弹对象调用检测的方法 将 潜艇对象传递进去
                    System.out.println("碰到了!!!");
                }
            }
        }
    }

深水炸弹打到潜艇,深水炸弹消失,潜艇消失。我们可以在SeaObject类中写一个godead方法。哪个对象打点调用godead方法,那个对象则设置为死亡状态。

 

 
 /**
     * 哪个对象打点调用狗带方法,那么就将当前对象设置为si亡状态。
     *
     * */
    public void goDead(){
        this.currentState = DEAD;//设置状态为si亡状态。
    }

然后再删除对象的代码中再加一个判断条件,当前对象若死亡,也需要删除。

 

 
 /**
     * 此方法是用来自动判断并删除越界对象的方法   放到run中调用
     */
    public void outOfBounds() {
        //1.遍历潜艇数组,并在循环里
        for (int i = 0; i < submarines.length; i++) {
            if (submarines[i].isoutBounds()||submarines[i].isDead() ) {//判断当前潜艇数组的每个对象是否越界或者是否死亡
                //2.若成立则将当前对象剔除出数组中。
                submarines[i] = submarines[submarines.length - 1];//将数组最后的元素赋值给当前越界的元素空间 (替换)
                submarines = Arrays.copyOf(submarines, submarines.length - 1);//删除最后一个空间 (缩容)
            }
        }
        //遍历雷数组
        for (int i = 0; i < thunders.length; i++) {
            if (thunders[i].isoutBounds()||thunders[i].isDead()) {
                thunders[i] = thunders[thunders.length - 1];
                thunders = Arrays.copyOf(thunders, thunders.length - 1);
            }
        }
        //遍历深水炸弹数组
        for (int i = 0; i < bombs.length; i++) {
            if (bombs[i].isoutBounds() || bombs[i].isDead()) {
                bombs[i] = bombs[bombs.length - 1];
                bombs = Arrays.copyOf(bombs, bombs.length - 1);
            }
        }
    }

接下来实现 雷与战舰的碰撞检测。

在GameWorld类中定义一个方法,方法名thunderBangAction 。放到run中调用。

遍历雷数组的对象依次跟战舰对象去检测。如果碰到了 雷消失。

 

 
 /**
     *  雷与战舰检测的逻辑     放 run中调用。
     * */
    public void thunderBangAction(){
        for (int i = 0; i < thunders.length; i++) {
             SeaObject t = thunders[i];//当前雷对象
            if(t.isLive() && t.isHit(ship)){//如果雷对象是活着的状态并且 雷对象与战舰碰到的话
                t.goDead();//当前雷对象消失
                //战舰减命
                ship.subtractLife();
            }
        }
    }

在深水炸弹与潜艇的碰撞测试代码中碰到的话加上加分的逻辑。

 

 
 int score = 0;//默认游戏分数为 0
    /**
     * 深水炸弹与潜艇碰撞检测的具体使用。  放run中调用。
     */
    public void bombBangAction() {
        for (int i = 0; i < bombs.length; i++) {//控制轮数
            Bomb b = bombs[i];//获取当前深水炸弹对象
            for (int j = 0; j < submarines.length; j++) {//次数
                SeaObject s = submarines[j];//获取当前潜艇数组中的对象
                //如果当前深水炸弹对象是活的 并且 当前潜艇对象也是活的 最后再去调用互相检测
                if (b.isLive() && s.isLive() && b.isHit(s)) {//当前深水炸弹对象调用检测的方法 将 潜艇对象传递进去
                    b.goDead();
                    s.goDead();
                    //加分  加命的方法
                    //判断s是不是 侦察潜艇类型
                    if(s instanceof ObserverSubmarine){//如果s对象是侦察潜艇的话
                        //则强转侦察潜艇类型  因为 s是SeaObject类型 不强转则无法调用getScore方法
                       ObserverSubmarine os =(ObserverSubmarine) s;
                       score += os.getScore();
                    } else if(s instanceof TorpedoSubmarine){//如果s对象是鱼雷潜艇的话
                        TorpedoSubmarine ts =(TorpedoSubmarine) s;
                        score += ts.getScore();
                    }
                }
            }
        }
    }

在paint方法中绘制:

 

 
         //绘制命数
        g.drawString("Life:" + ship.getLife(), 400, 50);
        //绘制分数
        g.drawString("Score:"+score,200,50);

在深水炸弹与潜艇碰撞具体逻辑里,不能判断具体的类型来实现加分或加命的行为,因为后续可能会新增新的潜艇,代码不易维护。

 

 
                   以下代码的问题:
                    复用性差   扩展性差    维护性差 (代码要遵循开闭原则 对扩展开放,对修改关闭)
                    加分  加命的方法
                    判断s是不是 侦察潜艇类型
                    if (s instanceof ObserverSubmarine) {//如果s对象是侦察潜艇的话
                        //则强转侦察潜艇类型  因为 s是SeaObject类型 不强转则无法调用getScore方法
                        ObserverSubmarine os = (ObserverSubmarine) s;
                        score += os.getScore();
                    } else if (s instanceof TorpedoSubmarine) {//如果s对象是鱼雷潜艇的话
                        TorpedoSubmarine ts = (TorpedoSubmarine) s;
                        score += ts.getScore();
                    } else if (s instanceof MineSubmarine) {
                        MineSubmarine ms = (MineSubmarine) s;
                        ship.setLife(ms.getLife());
                    }else if(s instanceof  以后的潜艇类型?){
                        ???
                    }

应修改为判断当前潜艇对象是否实现了加分或加命的接口,然后再去转换实现。

 

 
  if(s instanceof EnemyScore){//判断当前s这个潜艇对象 有没有实现EnemyScore接口
                        EnemyScore addScore= (EnemyScore)s;//直接把s强转成EnemyScore接口类型
                                //编译期间调父  运行时执行子。
                        score += addScore.getScore();//具体运行时使用的是侦察潜艇还是鱼雷潜艇 要看 地址指向的是哪个对象
                    } else if(s instanceof EnemyLife ){//判断当前s这个潜艇对象 有没有实现EnemyLife接口
                        EnemyLife addLife = (EnemyLife)s;
                        ship.setLife(addLife.getLife() );
                    }

游戏状态

在GameWorld类中需要定义三个状态,做三个常量 还有一变量表示当前游戏

 

 
    public static final int START = 0;//开始状态
    public static final int RUNNING = 1;//运行状态
    public static final int GAME_OVER=2;//结束状态
    private int currentState = START;//默认是开始状态

1.开始状态(Start):右键运行后呈现的界面,绘制一张开始界面的图片。当按下空格键则切换为运行状态。

 

 
   if (e.getKeyCode() == KeyEvent.VK_SPACE) {//判断用户按下的按键是不是空格键
                    if (currentState == START) { //当前状态是不是开始状态
                        currentState = RUNNING;//切换当前状态为运行状态
                    } else {//否则
                        bombEnterAction(); //发射深水炸弹
                    }
                }

2.运行状态(Running):调用所有自动行为的方法。

 

 
      if (currentState == RUNNING) { //如果当前状态是运行状态
                    submarineEnterAction();//调用潜艇入场的方法
                    thunderEnterAction();//调用雷入场的方法
                    stepAction();//调用移动的方法
                    outOfBounds();//调用删除优化对象的方法
                    bombBangAction();//调用深水炸弹与潜艇检测碰撞的方法
                    thunderBangAction();//调用雷与战舰的检测碰撞的方法
                    // System.out.println(submarines.length + "当前潜艇在内存中的数量" + ",雷在内存中的数量" + thunders.length);
                    repaint();//刷新绘制 每0.01s 执行一次
                }

3.结束状态(GameOver):当战舰的命数为0时,切换为结束状态。

 

 
  /**
     * 检测游戏是否结束的方法  ----实时检测 放 run中调用
     */
    public void checkGameOverAction() {
        if (ship.getLife() <= 0) {//如果战舰命数小于等于 0
            currentState = GAME_OVER;//切换状态为 游戏结束状态
        }
    }

在GameWorld类中paint的代码如下:

 

 
    @Override               //g 可以理解为画笔
    public void paint(Graphics g) { //JPanle 提供了绘制图片的方法
        //              1.填null 2.填g 3.0 4.0
        if (currentState == START) { //当前状态如果是开始状态
            ImageResources.start.paintIcon(null, g, 0, 0);
        } else if (currentState == RUNNING) {//当前状态如果是运行状态
            ImageResources.sea.paintIcon(null, g, 0, 0);
            ship.paintImage(g);//绘制战舰
            for (int i = 0; i < submarines.length; i++) {
                submarines[i].paintImage(g); //绘制潜艇数组中所有对象
            }
            for (int i = 0; i < thunders.length; i++) {
                thunders[i].paintImage(g);//绘制雷数组中 所有的对象
            }
            for (int i = 0; i < bombs.length; i++) {
                bombs[i].paintImage(g);//绘制深水炸弹数组中的所有对象
            }
            //绘制命数
            g.drawString("Life:" + ship.getLife(), 400, 50);
            //绘制分数
            g.drawString("Score:" + score, 200, 50);
        } else if (currentState == GAME_OVER) {
            ImageResources.gameover.paintIcon(null, g, 0, 0);
        }
    }

不退出从新开始的思路:

在游戏结束状态我想再次按下空格键 重新开始游戏!

2.上一次游戏结束时,所有用的数组中存在对象吗??若存在如何保证重新开始时 所有用到的数组重置 ?


基础知识的掌握:

1.声明个变量会不会?

2.数据类型 整数 : int long 小数:double 字符类型:char 布尔: boolean

3.使用运算符???

4. 分支: 单路分支 if(){ }

双路分支 if(){} else {}

多路分支: if(){} else if() {}else if() {}......

5.循环:for while do.. while

6. 声明个数组会不会 ?

7.定义方法会不会 ?

面向对象:

有三大特征

封装:

所有的学生对象都有 属性:姓名 年龄 学号 行为: 学习的行为

类的封装:封装同一类别下所有对象共有的属性的行为。

方法的封装:封装某个功能逻辑,达到复用(一次定义,多次使用)

属性的封装:属性私有化,方法公开化 (目的:保护程序(属性)防止任意地方修 改!)

继承:好处:实现代码的复用,

坏处:耦合性高!(父类只放子类共有的属性和行为)

接口:如果多个类别之间 仅仅只是行为一致!我们推荐使用接口来去当代表。

多态:在一个类别下 的不同实现

声明父 new 子 子类不同实现!


API (Application Programing Interface)应用程序接口(功能),我们讲解Java中最常用的工具(功能)。

什么是API:API表示的就是功能,我们可以通过学习API 快速的进行我们编程思路的开发。

API的实现初衷:设计思路就是将复杂的业务逻辑 封装成功能,供调用者更好的使用。对于使用者而言,不需要关注功能内部的实现细节,只需要知道如何使用即可。

使用API(功能),有的功能需要导包,最常用的包:

1.java.lang包:java语言的核心语法,内部封装了java最核心的API.所以一般在使用lang包下的内容的时候,不需要导包。

例如:String 、 Object 、System 、Integer..................

2.java.io 包:可以实现文件操作,输入输出等功能。

3.java.net : 网络相关的功能

4.java.util:工具包

String (字符串)

1.java.lang.String这个类用final来修饰的,不能被继承。

2.String类中维护了一个不可变(用final修饰)的char数组!

3.字符串一旦创建,对象的内容是不可变,若重新为字符串的变量赋值,则表示一个新对象。

4.String 是一种数据类型,是引用类型。

 

 
String name = "陈晓东";//声明一个String类型的变量 name里面存 了"陈晓东"字符串对象 //推荐的形式
String name1 = new String("陈晓东");//但是这种方式Java不推荐
char[] chs ={'陈','晓','东'};
上述代码 name 和 chs 在内存引用的内容是同一个。

String一旦被创建则不可变,原因在于String非常的常用,这么设计就是为了优化内存,在堆中的区域存在一个常量池,池子里存放的是已被创建过的字符串地址。

 

 
        String s1 = "ABC";
        String s2 = "ABC";
        System.out.println(s1 == s2);

String可以复用的条件,String创建对象的形式分为两种:

静态字符串创建方式可以实现复用:

1.以字符串字面量的形式创建:例如 "ABC" 、 "hello world".....

2.字符串的常量

3.字符串的字面量拼接成的字符串

 

 
    public static final String S3 = "ABC"; //字符串常量 可以进行常量池复用
    public static void main(String[] args) {
//        String s1 = "ABC";//使用字面量  创建了一个String 对象
//        String s2 = s1;
//        s1 = s1 + "DEF";
//        System.out.println(s1);
//        System.out.println(s2);
        //可以复用的情况测试:
        String s1 = "ABC";
        String s2 = "ABC";
        String s4 = "A" + "BC"; //字符串字面量拼接 可以复用
        System.out.println(s1 == s2);
        System.out.println(S3 == s1);
        System.out.println(s1 == s4);

动态字符串创建方法不可复用:

1.使用new关键字创建的字符串 : String s = new String("ABC");

2.String变量 拼接 字面量

 

 
  //不可复用的情况测试:
        String s5 =new String("ABC");  //不推荐
        String s6 = new String(new char[]{'A','B','C'}); //不推荐
        System.out.println(s5 == s1);
        String s7 = "A";
        String s8 = s7 + "BC";
        System.out.println(s8 == s1);
    }

面试题:

String str = new String("ABC");

问:上述代码产生了多少个String对象!

答:两个String对象

如果问的是创建多少个对象,答:两个String对象 一个char数组对象。

StringAPI

String类中,提供了一些方法(API),可以获取字符串的长度,获取字符串中某个下标的字符,转换大写,转换小写....

1.获取字符串中的长度,通过下标获取字符串中某个字符

 

 
        //1.获取字符串的长度 和通过下标获取字符串中对应的字符
        String str = "Thinking in java是一本好书";
        int length = str.length();// 获取字符串长度
        System.out.println(length);
        char c = str.charAt(20);// 通过下标获取字符串中对应的字符
        System.out.println(c);
        /**
         *  面试题:请写一段程序,测试出 "Thinking in java是一本好书" 里面 字符i存在的次数
         * */
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
             if(str.charAt(i) == 'i'){//判断当前字符是否 等于 字符 i
                count++;//计数
             }
        }
        System.out.println("i存在的次数:"+count);

2. indexof() 从头向后 检索字符或字符串 在当前这个字符串中的位置

lastIndexof() 从后向前 检索字符或字符串 在当前这个字符串中的位置

检索到传入的字符或者字符串 后,立刻返回对应的下标。

 

 
  //2. indexof 从头向后开始检索
        String str = "Thinking in java是一本好书";
        int index = str.indexOf('i');//通过传入的字符 返回字符在字符串中存在的下标
        System.out.println(index);
        int index1 = str.indexOf('x');//如果找不到 则返回-1
        System.out.println(index1);
        int index2 = str.indexOf('i', 3);//可以指定起始检索位置向后检索字符
        System.out.println(index2);//5
        int index3 = str.indexOf("java");
        System.out.println(index3);
        //lastIndexOf 从后往前开始检索  无非检索的方向发生的变化 其它的功能使用与index是一致的。
        int index4 = str.lastIndexOf('i');
        System.out.println(index4);

3.转换当前字符串的大小写方法

 

 
//3.转换大小写
        String str = "Thinking in java是一本好书";
        str = str.toUpperCase();//字符串字母内容转大写
        System.out.println(str);
        str = str.toLowerCase();//字符串字母内容转小写
        System.out.println(str);
        //4.去除两端空白
        String name = " Tom";
        System.out.println(name);
        name = name.trim();//去除字符串两端空白
        System.out.println(name);
        //5.是否是以什么开头 或 什么结尾的方法
        boolean r = name.startsWith("T");//判断是否是以xx开头的字符串内容
        System.out.println(r);
        boolean r1 = name.endsWith("m");//判断是否是以xx结尾的字符串内容
        System.out.println(r1);

4.subString();截取字符串

 

 
   String str = "bjzhangpeng@tedu.cn";
//        String user = str.substring(7);//截取字符串,填入截取起始下标位置,从此位置向后全部截取
        int endIndex = str.indexOf('@');
        String user = str.substring(0,endIndex);//截取字符串 不包括结束索引的位置(包头不包尾)
        System.out.println(user);
        //截取字符串的用法:
        //1.substring(开始索引,结束索引); 会截取出区域的字符串内容.
        //2.subString(开始索引,开始索引+要截取的长度);
        //为什么不包含结束位置,其实就是为了方便以后按照长度进行截取.
        String test = str.substring(0,0+5);
        System.out.println(test);

5.valueOf();

 

 
//5.valueof();这是String类提供的静态方法,主要的作用就是可以将数值类型的变量转换为字符串
int a = 100;
String b = String.valueOf(a);
System.out.println(b);//"100"
System.out.println(100);//打印语句中放的任意的值类型数据 其实都是做了String.valueof的处理来显示的.

6.contains 判断是否包含XX内容的方法使用

 

 
//6.contains 判断是否包含XX内容的方法使用
String c = "AOP";
boolean r = c.contains("A");//判断传入的字符串内容是否存在当前这个变量c中
System.out.println(r);

String 不适合频繁修改,一旦发生修改,就是新创建对象,若频繁的使用String修改,对内存的消耗非常大!容易出现异常,java.lang.OutOfMemoryError 内存溢出。

StringBuilder

解决当频繁的对字符串进行修改时,可以使用Java提供的StringBuilder

因为StringBuilder内部维护了可变的char数组,修改性能小,提供很多字符串修改相关的操作。

增删改插

 

 
  //增
        String str = "好好学习Java!";
        StringBuilder builder = new StringBuilder(str);//创建对象并传入要处理的字符串对象
        //append 字符串追加
        builder.append("为了找份好工作");
        System.out.println(builder);
        //改
        //好好学习Java!为了找份好工作  修改为好好学习Java!为了改变世界
        //replace 替换   1. 替换开始索引  2.结束的索引(包头不包尾)  3.替换的内容
        builder.replace(9, 16, "为了改变世界");
        System.out.println(builder);
        //删
        //delete 1.从哪里开始索引删除   2.到哪里结束索引(包头不包尾)
        builder.delete(0,9);
        System.out.println(builder);
        //插
        //insert  1.从哪里开始插入(索引)  2.插入的内容
        builder.insert(0,"活着");
        System.out.println(builder);
        //字符串内容反转
        //reverse反转
        builder.reverse();
        System.out.println(builder);
        str = builder.toString();//将处理好的字符串对象 再赋值给 str
        System.out.println(str);

正则表达式

正则表达式可以用来检测格式是否正确的功能,例如:邮箱的格式,手机号,身份证号格式...都可以用正则来进行约束用户输入的数据。

基本正则表达式:

[] 表示的是一个字符的意思。

[a,b,c] 表示可以写任意一个 a 或 b 或 c 其中一个字符。

[^abc] 表示的是除了a 或 b 或 c 之外的任意一个字符串。

[a-z] 表示的意思是可以任意写一个 26个小写字母其中一个字符

[a-zA-Z0-9] 表示的是可以写 任意一个26个小写字母或26个大写字母或0-9的数字。

 

 
[a-z&&[^bc]] 表示的是 26个小写字母中,除了bc 不可以写,其它都可以写

预定字符集

. 表示任意一个字符

\d 表示可以写任意一个数字字符 --> [0-9]

\w 表示单个词的字符 -----> [a-zA-Z0-9]

\s 表示是空白字符

\D 表示的非数字字符

\W 表示非字母或数字的字符

\S 表示是非空白的字符

数量词

 

 
?   代表可以写 0 - 1次
    例如:[abc]? 表示可以写一个 a 或 b 或 c,不能写其它的字符内容。
+   代表至少出现1次以上 没有上限
     例如:[abc]+ 表示至少写一个a 或 b 或 c内容,可以出现多次,但不能写其它的字符内容。
*   代表任意次,一次不写也可以    
---------------------------
限制次数 
        [abc]{3} 代表写3个字符
        [abc]{3,5} 代表至少写3个 最多写5个 
        [abc]{3,}  代表至少写3次 没有上限

分组

 

 
(abc){3}  表示:可以视为abc为一组  至少出现3组的意思
(abc|def){3}    表示:可以视为abc 或 def 为一组 至少出现3组的意思

正则表达式判断是否是邮箱:

 

 
//        String email = "bjzhangpeng@tedu.com.cn";
//        String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
//        boolean match = email.matches(regex);//判断传入的正则表达式的格式 是否与 当前字符串对象中的邮箱格式匹配
//        if(match){
//            System.out.println("是邮箱");
//        }else {
//            System.out.println("不是邮箱!");
//        }

正则表达式切割的使用方式:

 

 
 String str = "abc123def456ghi";
                                        //0-9的数字出现一个或多个的地方来进行切割
        String[] data = str.split("[0-9]+");//传入正则表达式 根据正则的格式来进行切割
        for (int i = 0; i < data.length; i++) {
           // System.out.println(date[i]);
        }
        String str1 = "a,b,c,d,e,f";
        String[] data1 = str1.split(",");//也可以传入字符串的内容  根据具体的内容来进行切割
        for (int i = 0; i < data1.length; i++) {
            System.out.println(data1[i]);
        }

正则表达式替换的使用方式:

 

 
 String regex = "(sb|tmd|nc|wr|qnmd|dsb)";
        String message= "qnmd!你个dsb,你怎么这么nc!";
                                 //1.传入需要匹配的正则格式
                                // 2.若匹配时所替换的内容
        message = message.replaceAll(regex,"小可爱");
        System.out.println(message);
//        message = message.replaceAll(regex, "小可爱");
//        System.out.println(message);
        String str2 = "abc12de3f456";
        str2 = str2.replaceAll("[0-9]+", "NUMBER");
      //  System.out.println(str2);
        //replaceAll 支持正则的替换功能 , 也支持第一个参数填入具体的内容,匹配则全部替换。
        //replace   不支持正则的替换功能, 只要被替换的内容存在,则全部替换。
        String str3 ="abc123def123";
               //replace  方法   1.需要被替换的内容
                              //2. 实际替换的内容
        str3 = str3.replace("123","456");
        System.out.println(str3);

Object

Object是所有类的直接父类或间接父类。

我们所创建的类,默认继承了Object这个类。

为什么需要Object类,因为Object类承载了所有类的共性内容,为Java提供一系列的基础的结构支撑,是所有的类的(父类/基类)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值