JAVA-第一阶段

目录

一、java概述

二、转义字符

三、变量

四、数据类型

1、整型的使用细节       2、浮点类型       3、Java API文档           4、字符类型 char

5、字符编码表              6、布尔类型       7、基本数据类型转换    8、强制类型转换

五、运算符

1、算术运算符        2、关系运算符         3、逻辑运算符                4、赋值运算符

5、三元运算符        6、运算符优先级     7、标识符规则与规范      8、关键字与保留字

六、键盘输入

七、进制

1、进制转换          2、原码 反码 补码          3、位运算符

八、程序流程控制

1、分支控制           2、嵌套分支       3、switch分支结构       4、循环控制-for循环

5、循环控制-while循环        6、do…while循环       7、多重循环          8、化繁为简、先死后活

9、跳转控制语句break       10、跳转控制语句continue         11、跳转控制语句return       12、程序控制结构作业

九、数组

1、数组的使用方式       2、数组使用注意事项和细节       3、赋值机制与数组拷贝

4、数组反转输出           5、数组扩容                   6、数组缩减                  7、排序

十、二维数组

1、二维数组在内存中的存在形式        2、二维数组的使用方式

3、二维数组使用细节和注意事项        4、二维数组练习

十一、面向对象编程

1、类与对象          2、对象在内存中的存在形式        3、属性          4、方法

5、成员方法的定义及细节          6、方法调用细节说明       7、成员方法传参机制       8、克隆对象

十二、递归

十三、方法的重载

十四、可变参数

十五、作用域

十六、构造器

1、注意事项和使用细节          2、创建对象流程分析         3、流程分析(面试题)

十七、this

1、this的注意事项和使用细节           2、匿名对象          3、练习题

十八、IDEA的使用

十九、包

二十、访问修饰符

二十一、封装介绍

二十二、继承

1、继承细节问题            2、继承原理的本质分析          3、继承课堂练习

二十三、super关键字

1、super细节         2、super和this的比较

二十四、方法重写/覆盖

1、方法重写的细节         2、方法的重载和重写比较

二十五、多态

1、多态详解              2、向上转型         3、向下转型

4、多态注意事项       5、练习题             6、动态绑定机制

二十六、多态数组

二十七、多态参数

二十八、== 和 equals 的对比

二十九、hashCode方法

三十、toString方法

三十一、finalize方法

三十二、断点调试

三十三、零钱通案例

三十四、房屋出租系统

三十五、练习题

------------------------------------------------------------------------------

一、java概述

1、Java技术体系平台

Java SE(Java Standard Edition)标准版

支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,此版本以前称为J2SE

Java EE(Java Enterprise Edition)企业版

是为开发企业环境下的应用程序提供的一套解决方案。该技术体系中包含的技术如:Servlet、Jsp等,主要针对于Web应用程序开发,版本以前称为J2EE,学习java EE是要以java SE为基础的

Java ME(Java Micro Edition)小型版

支持Java程序运行在移动终端((手机、PDA)上的平台,对Java API有所精简,并加入对移动终端的支持,此版本以前称为J2ME

2、java虚拟机

JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器,包含在JDK中

对于不同的平台,有不同的虚拟机,Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”,所以才说java的跨平台性极好

3、JDK与JRE

JDK的全称(Java Development Kit Java开发工具包)

JDK=JRE +java的开发工具 [ java, javac,javadoc,javap 等]

JDK是提供给Java开发人员使用的,其中包含了java 的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。

JRE的全称(Java Runtime Environment Java运行环境)

JRE=JVM +Java的核心类库 [ 类 ] 

包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

JDK、JRE和JVM的包含关系

①JDK=JRE+开发工具集(例如Javac,java编译工具等)

②JRE=JVM +Java SE标准类库(java核心类库)

③如果只想运行开发好的.class文件只需要JRE

4、下载安装JDK

官方网址:https://www.oracle.com/java/technologies/javase-downloads.html

安装完之后需要配置环境变量

环境变量原理:当前执行的程序在当前目录下如果不存在,win10系统会在系统中已有的一个名为path的环境变量指定的目录中查找。如果仍未找到,会出现错误提示

所以需要将java用到的程序路径存入到环境变量中去

1、我的电脑--属性--高级系统设置--环境变量
2、增加JAVA HOME 环境变量,指向jdk的安装目录
3.编辑path 环境变量,增加%JAVA HOME%\bin
4、打开DOS命令行,任意目录下敲入javac、java,如果出现javac的参数信息,配置成功

centos安装java11

rpm -qa | grep java  #查询系统是否已经安装java

卸载安装的java

rpm -e --nodeps java-11-openjdk-devel-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-headless-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-static-libs-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-javadoc-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-jmods-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-src-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps java-11-openjdk-demo-11.0.15.0.9-2.el7_9.x86_64
rpm -e --nodeps javapackages-tools-3.4.1-11.el7.noarch
rpm -e --nodeps java-11-openjdk-javadoc-zip-11.0.15.0.9-2.el7_9.x86_64

#验证一下是还有jdk
rpm -qa | grep java
java -version
#没有内容证明已经卸载干净了
#yum源搜索java-11的安装包
yum search java-11

yum install java-11* -y  #全部安装
java -version            #验证安装结果

查看安装的目录,并设置JAVA_HOME

whereis java
ll -a /usr/bin/java
ll -a /etc/alternatives/java

java-11的安装本地目录为:/usr/lib/jvm/java-11-openjdk-11.0.15.0.9-2.el7_9.x86_64/

添加环境变量,设置设置JAVA_HOME

vi /etc/profile

在文件最后添加以下代码

source /etc/profile   #刷新profile文件

5、java快速入门

//这是我的第一个java程序
//public class Hello表示Hello是一个类,是一个public公有的类
//Hello{ }表示一个类的开始和结束
//public static void main(String[] args)表示一个程序的主方法,及程序的入口
//main() { }表示方法的开始和结束
//System.out.println("Hello,World!")表示输出Hello,World!到屏幕上
//“;”表示语句的结束
    
public class Hello {
    //编写一个main方法
    public static void main(String[] args) {
        System.out.println("Hello,World!");
    }
}

 

6、java开发注意事项

1>Java源文件以.java为扩展名。源文件的基本组成部分是类(class),如本类中的Hello类

2>Java应用程序的执行入口是main()方法。

它有固定的书写格式:

public static void main(String[] args) {...}

3>Java语言严格区分大小写

4>Java方法由一条条语句构成,每个语句以“;”结束

5>大括号都是成对出现的,缺一不可,要习惯先写 { } 再写代码

6>一个源文件中最多只能有一个public类,其它类的个数不限

7>如果源文件包含一个public类,则文件名必须按该类名命名

8>一个源文件中最多只能有一个public类,其它类的个数不限,也可以将main方法写在非public类中,然后指定运行非public类,这样入口方法就是非public的main方法

7、如何快速掌握新技术

二、转义字符

\t #一个制表位,实现对齐的功能
\n #换行符
\\ #输出一个\
\" #输出一个"
\' #输出一个'
\r #一个回车

 示例

public class ChangeChar {
    public static void main(String[] args) {
        System.out.println("莫使\t金樽\t空对月");
        System.out.println("人生得意\n须尽欢");
        System.out.println("C:\\Windows\\case\\test.txt");
        System.out.println("李白说:\"莫使金樽空对月\"");
        System.out.println("***莫使金樽空对月\r李白=");  //\r\n表示回车加换行
    }
}

代码注释分为javadoc注释和单行/多行注释

//    单行注释

/*
多行注释
*/

javadoc注释被称为文档注释,注释内容可以被JDK提供的工具javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档,一般写在类

javadoc -d 文件夹名 -author -version char.java

java开发简单规范

1、类、方法的注释,要以javadoc的方式来写注释

2、非Javadoc的注释,往往是给代码的维护者看的,着重告述读者为什么这样写,如何修改,注意什么问题等(单行,多行注释)

3、使用tab操作,实现缩进,默认整体向右边移动;用shift+tab整体向左移

4、运算符和=两边习惯性各加一个空格。比如:2 + 4 * 5 + 345 - 89

5、源文件使用utf-8编码

6、行宽度不要超过80字符

7、代码编写次行风格和行尾风格,推荐使用行尾风格

三、变量

变量是程序的基本组成单位,变量有三个基本要素(类型+名称+值),变量相当于内存中一个数据存储空间的表示

class variable {
    public static void main(String[] args) {
        //定义变量先
        int c;
        //再给变量赋值
        c = 9527;
        System.out.println(9527);
        //还可以定义变量和赋值一步到位
        int a = 1;
        System.out.println(a+c);
    }
}

变量使用注意事项

1、变量表示内存中的一个存储区域,不同的变量,类型不同,占用的空间大小不同

2、该区域有自己的名称[变量名]和类型[数据类型]

3、变量必须先声明,后使用,即有顺序

4、该区域的数据/值可以在同一类型范围内不断变化

5、变量在同一个作用域内不能重名

6、变量=变量名+值+数据类型,即变量三要素

程序中+号的使用

1、当左右两边都是数值型时,则做加法运算

2、当左右两边有一方为字符串,则做拼接运算

3、运算顺序从左到右

class plus {
    public static void main(String[] args) {
        System.out.println(100+98);   //198
        System.out.println("100"+98);  //10098
        System.out.println(100+98+"hello");  //198hello
        System.out.println("hello"+100+98);  //hello10098
    }
}

四、数据类型

每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间(字节)

class bytes_test {
    public static void main(String[] args) {
        byte n = 200; //故意设置出超过此范围的数,查看会报什么错误
        System.out.println(n);
    }
}

1、整型的使用细节

1> Java各整数类型有固定的范围和字段长度,不受具体OS[操作系统]的影响,以保证java程序的可移植性

2> Java的整型常量(具体值)默认为int型,声明long型常量须后加l或L

3> java程序中变量常声明为int型,除非不足以表示大数,才使用long

4> bit:计算机中的最小存储单位; byte:计算机中基本存储单元,1byte = 8 bit

2、浮点类型

Java的浮点类型可以表示一个小数,比如123.4 、7.8 、0.12等等

1> 单精度与双精度的区别

在内存中占有的字节数不同:单精度浮点数在机内占4个字节,双精度浮点数在机内占8个字节;

有效数字位数不同:单精度浮点数有效数字8位,双精度浮点数有效数字16位。

关于浮点数在机器中存放形式的简单说明:浮点数=符号位+指数位+尾数位,尾数部分可能丢失,造成精度损失(小数都是近似值)

2> 使用细节

与整数类型类似,Java浮点类型也有固定的范围和字段长度,不受具体OS的影响。[float 4个字节、double是8个字节]

Java 的浮点型常量(具体值)默认为double型,声明float型常量,须后加‘f‘或‘'F'

class folat_test {
    public static void main(String[] args) {
    float n = 3.14F;
    double m = 3.14;
    System.out.println(n+" -- "+m);
    }
}

3> 浮点型常量有两种表示形式

十进制数形式,如:5.12、512.0f、.512(必须有小数点)

科学计数法形式,如:5.12e2 [5.12*10的2次方]、5.12E-2 [5.12/10的2次方]

4> 通常情况下,应该使用double型,因为它比float型更精确

class double_duibi {
    public static void main(String[] args) {
    double num9 = 2.1234567851;
    float num10 =2.1234567851F;
    System.out.println(num9);
    System.out.println(num10);
    }
}

5> 浮点数使用陷阱:2.7和8.1/3比较

class trap {
    public static void main(String[] args) {
        double num1 = 2.7;
        double num2 = 8.1/3;
        System.out.println(num1);
        System.out.println(num2);
    }
}

所以得到一个重要的使用点:当我们对运算结果是小数的进行相等判断时,要小心,应该是以两个数的差值的绝对值,在某个精度范围类判断

但如果是直接查询得的小数或者直接赋值,则可以直接判断相等

class trap {
    public static void main(String[] args) {
        double num1 = 2.7;
        double num2 = 8.1/3;
        if( Math.abs(num1 - num2) < 0.00001 ) {
            System.out.println("相等");
        }
    }
}

3、 Java API文档

1> API (Application Programming Interface,应用程序编程接口)是Java提供的基本编程接口(java提供的类还有相关的方法)

中文在线文档:https://www.matools.com

2> Java语言提供了大量的基础类,因此 Oracle 公司也为这些基础类提供了相应的API文档,用于告诉开发者如何使用这些类,以及这些类里包含的方法

3> Java类的组织形式

4、字符类型 char

字符类型可以表示单个字符,字符类型是char,char是两个字节(可以存放汉字),多个字符我们用字符串String

1> 字符常量是用单引号(‘ ’)括起来的单个字符。例如:char v1 = 'a'、char v2 = '辉'、char v3 = '9'

2> Java中还允许使用转义字符来将其后的字符转变为特殊字符型常量。例如:char v3 = ‘\n'  '\n'表示换行符

3> 在java中, char的本质是一个整数,在输出时,是unicode码对应的字符

4> 可以直接给char赋一个整数,然后输出时,会按照对应的unicode字符输出

5> char类型是可以进行运算的,相当于一个整数,因为它都对应有unicode码.

public class test {
    public static void main(String[] args) {
        char v1 = 'a';
        char v2 = 98;
        char v3 = 'a' + 2;
        char v4 = '赵';
        char v5 = '\t';
        char v6 = '王';
        System.out.println(v1+" "+v2+" "+v3);   //a b c
        System.out.println(v1+v2+v3);   //294
        System.out.println((int)v1);    //97
        System.out.println(v4);         //
        System.out.println(v5);         //换行符
        System.out.println(v4 + v6);    //65792    v4和v6本质上是整数,所以结果为将他的码值加起来
    }
}

字符型存储到计算机中,需要将字符对应的码值(整数)找出来,比如 'a'

存储:'a' ==> 码值97 ==> 二进制(110 0001)  ==> 存储

读取:二进制(110 0001) ==> 97 ==> 'a' ==> 显示

5、字符编码表

ASCII:ASCIl编码表一个字节表示,一个128个字符,实际上一个字节可以表示256个字符,只用了128个

Unicode:Unicode编码表固定大小的编码使用两个字节来表示字符,字母和汉字统一都是占用两个字节,这样浪费空间

utf-8:大小可变的编码表,字母使用1个字节,汉字使用3个字节

gbk:可以表示汉字,而且范围广,字母使用1个字节,汉字2个字节

gb2312:可以表示汉字,gb2312< gbk

big5码:繁体中文、台湾、香港

ASCII码介绍

上个世纪60年代,美国制定了一套字符编码(使用一个字节),对英语字符与二进制位之间的关系,做了统一的规定。这被称为ASCII码

ASCII码一共规定了128个字符的编码,只占用了一个字节的后面7位,最前面的1位统一规定为0

特别提示:一个字节可以表示256个字符,ASCII码只用了128个字符.

缺点:不能表示所有字符。

Unicode编码介绍

Unicode的好处:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,使用Unicode没有乱码的问题

Unicode的缺点:一个英文字母和一个汉字都占用2个字节,这对于存储空间来说是浪费

2的16次方是65536,所以汉字最多编码是65536个字符

编码0-127的字符是与ASCII的编码一样.比如'a'在ASCII码是0x61,在unicode码是ox0061,都对应97.因此Unicode码兼容ASCII码.

UTF-8编码介绍

UTF-8是在互联网上使用最广的一种 Unicode的实现方式(改进)

UTF-8是一种变长的编码方式。它可以使用1-6个字节表示一个符号,根据不同的符号而变化字节长度

使用大小可变的编码字母占1个字节,汉字占3个字节

6、布尔类型

布尔类型也叫boolean类型,在java中booolean类型数据只允许取值true和false,无null,同时不能使用其他字符代替true或false

boolean类型占1个字节,boolean类型适于逻辑运算,一般用于程序流程控制

public class test {
    public static void main(String[] args) {
        boolean var = true;
        if(var == true) {
            System.out.println("This is True.");
        } else {
            System.out.println("This is False");
        }
    }
}

7、基本数据类型转换

当java程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换。

数据类型按精度(容量)大小排序为

int a = 'a';
double = 80;

1> 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算

2> 当我们把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时,就会报错,反之就会进行自动类型转换

3> (byte, short)和char之间不会相互自动转换

4>  byte,short,char他们三者可以计算,在计算时首先转换为int类型

5> boolean不参与转换

6> 自动提升原则:表达式结果的类型自动提升为操作数中最大的类型

//代码示例1
public class type_change {
    public static void main(String[] args) {
        byte a = 10;
        int b = 580;
        double c = 1314.20;
        double res = a + b + c;
        System.out.println(res);
    }
}
//代码示例2
public class type_change {
    public static void main(String[] args) {
        double c = 1314;
        System.out.println(c);
    }
}
//代码示例3
public class type_change {
    public static void main(String[] args) {
        byte a = 13;
        short b = 200;
        char c = 'a';
        int res = a + b + c;
        System.out.println(res);
    }
}

8、强制类型转换

自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出。

public class type_change {
    public static void main(String[] args) {
        int a = 9527;
        byte res = (byte)a;
        System.out.println(res);
    }
}

上图的res结果为55,int类型的9527转为二进制为:00000000 00000000 00100101 00110111,因为byte为一个字节,所以只会保留上述二进制中的最后8位,即00110111=55,这种现象称为精度降低

强制类型转换细节说明

1> 当进行转换的数据精度从大——>小,就需要使用到强制转换

2> 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

3> char类型可以保存int的常量值,但不能保存int的变量值(byte同理),需要强转,因为变量的大小之前是固定好了的

4> byte和short、char类型在进行运算时,当做int类型处理

5> byte a = 100;在给变量a赋值的时候,首先会判断100在不在byte的范围内,如果在的话则就赋值,不在则报错。

#案例演示1
public class type_change {
    public static void main(String[] args) {
        int res = (int)10*3.5;  //会报错
        int res = (int)(10*3.5);
        System.out.println(res);
    }
}
//案例演示2
public class type_change {
    public static void main(String[] args) {
        int a = 97;  
        char b = 97;  //char可以保存int的常量值
        char res = (char)a;  //如果保存int的变量值则需要强转
        System.out.println(res);  //打印的是97对应的字母
    }
}
//案例演示3
public class type_change {
    public static void main(String[] args) {
        byte a = 97;  
        short b = 97;
        char c = 97;
        int res = a + b + c; 
        System.out.println(res);   //结果:291
    }
}

基本数据类型和String类型的转换

在程序开发中,我们经常需要将基本数据类型转成String类型。或者将String类型转成基本数据类型。

基本类型转String类型语法:将基本类型的值+""即可

public class HelloWorld {
    public static void main(String []args) {
        int a = 9527;
        String str = a + "";
        System.out.println(str);      //9527
        System.out.println(str + 1);  //95271
    }
}

String类型转基本数据类型

语法:通过基本类型的包装类调用parseXX方法即可

public class HelloWorld {
    public static void main(String []args) {
        String str = "88";
        int a = Integer.parseInt(str);
        byte b = Byte.parseByte(str);
        short c = Short.parseShort(str);
        long d = Long.parseLong(str);
        float e = Float.parseFloat(str);
        double f = Double.parseDouble(str);
        boolean g = Boolean.parseBoolean("true");
        System.out.println(a + 1);
        System.out.println(b + 1);
        System.out.println(c + 1);
        System.out.println(d + 1);
        System.out.println(e);
        System.out.println(f);
        System.out.println(g);
    }
}

注意事项

在将String类型转成基本数据类型时,要确保String类型能够转成有效的数据,比如我们可以把“123",转成一个整数,但是不能把"hello”转成一个整数,如果格式不正确,就会抛出异常,程序就会终止。

 

public class test {
    public static void main(String[] args) {
        String name = "赵大大";
        short age = 22;
        float grade = 99.99F;
        char gender = '男';
        String hobby = "女";
        System.out.println("姓名\t年龄\t成绩\t性别\t爱好\n" + 
                           name + "\t" + age + "\t" + grade + "\t" + gender + "\t" + hobby);
    }
}

五、运算符

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。

算术运算符、赋值运算符、关系运算符[比较运算符]、逻辑运算符、位运算符(需要二进制基础)、三元运算符

1、算术运算符

算术运算符是对数值类型的变量进行运算的,在Java程序中使用的非常多。

// /的使用
public class HelloWorld {
    public static void main(String []args) {
        System.out.println(10 / 4);  //2  因为10是int,4也是int,所以结果也是int
        System.out.println(10.0 / 4);  //2.5   运算式中最高级为double,所以结果也为double
        double a = 10 / 4;
        System.out.println(a);  //2.0
    }
}
// %的使用
%的本质:a - (a / b)取整 * b
public class HelloWorld {
    public static void main(String []args) {
        System.out.println(10 % 3); //10 - (10 / 3)的取整 *3 = 10 - 9 = 1
        System.out.println(-10 % 3); //-10 - (-10 / 3)的取整 *3 = -10 + 9 = -1
        System.out.println(10 % -3); //10 - (10 / -3)的取整 *(-3) = 10 - 9 = 1
        System.out.println(-10 % -3); //-10 - (-10 / -3)的取整 *(-3) = -10 + 9 = -1
    }
}
// ++的使用
//作为独立语句使用时:i++、++i完全等价于i = i + 1
public class HelloWorld {
    public static void main(String []args) {
        int i = 10;
        i++; 
        System.out.println("i =" + i);  //11
        ++i;
        System.out.println("i =" + i);  //12
    }
}
//作为表达式使用
++i表示前++,先自增,后赋值
i++表示后++,先赋值,再自增
public class HelloWorld {
    public static void main(String []args) {
        int i = 10;
        int k = i++; 
        System.out.println("i=" + i + "  k=" + k);  //i=11  k=10
        i = 10;
        k = ++i; 
        System.out.println("i=" + i + "  k=" + k);  //i=11  k=11
    }
}
if (i++==6){…}  //这里因为是后++,所以是先比较,后自增,这是一个表达式,是判断是否等于的

1> 对于除号"/",它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。例如:int x= 10/3 ,结果是3

2> 当对一个数取模时,可以等价 a % b = a - a / b * b,这样我们可以看到取模的一个本质运算。(a/b是java中的除法特点)

3> 当自增作为一个独立语言使用时,不管是++i、还是i++都是一样的,等价于i = i+ 1

4> 当自增作为一个表达式使用时:j = ++i 等价 i = i + 1、j = i

5> 当自增作为一个表达式使用时:j = i++ 等价 j = i、i = i + 1

算术运算符练习题

public class run {
    public static void main(String[] args) {
        int i = 1;
        i = i++;   //规则使用临时变量:(1) temp=i、(2) i=i+1、(3)i=temp
        System.out.println(i);  //打印结果:1
        int k = 1;
        k = ++k;   //规则使用临时变量:(1) k=k+1、(2) temp=k、(3)k=temp
        System.out.println(k); //打印结果:2
    }
}
//定义一个变量保存华氏温度,华氏温度转换摄氏温度的公式为: 5/9*(华氏温度-100),请求出华氏温度对应的摄氏温度。[234.5]
public class test {
    public static void main(String[] args) {
        double huashi = 234.5;
        double sheshi = 5.0 / 9 * (huashi - 100);  //注意这里必须是5.0,不然在java里5/9=0的
        System.out.println(sheshi);
    }
}    
(int)(Math.random()*100) + 1;  //生成1个1-100的随机数
Math.random()方法可以生成[0, 1)之间的数字,如果用这个数字乘以3就是一个从[0, 3)之间的数字

2、关系运算符

关系运算符也叫比较运算符,关系运算符的结果都是boolean型,也就是要么是true,要么是false

关系表达式经常用在if结构的条件中或循环结构的条件中

public class HelloWorld {
    public static void main(String []args) {
        int number_first = 10;
        int number_second = 20;
        System.out.println(number_first == number_second);    //false
        System.out.println(number_first != number_second);    //true
        System.out.println(number_first < number_second);     //true
        System.out.println(number_first > number_second);     //false
        System.out.println(number_first <= number_second);    //true
        System.out.println(number_first >= number_second);    //false
        boolean flog = number_first != number_second;  //可以将关系表达式的结果赋给布尔类型的变量
        System.out.println(flog);        //true
    }
}

1> 关系运算符的结果都是boolean型,也就是要么是true,要么是false

2> 关系运算符组成的表达式,我们称为关系表达式:a >b

3> 比较运算符"=="不能误写成"=","=="为等于。"="为赋值

3、逻辑运算符

用于连接多个条件(多个关系表达式),最终的结果也是一个boolean值。

逻辑运算符一览

短路与:&&

短路或:||

逻辑与:&

逻辑或:|

取反:!

逻辑异或:^

a & b : & 叫逻辑与,当a 和 b 同时为true,则结果为true,否则为 false

a && b : && 叫短路与,当a 和 b 同时为true,则结果为true,否则为 false

a l b:| 叫逻辑或,当a和 b ,有一个为true,则结果为true,否则为false

a || b :叫短路或,当a和 b,有一个为true,则结果为true,否则为false

! a:叫取反,或者非运算。当a为true,则结果为false;当a 为 false,结果为true

a ^ b:叫逻辑异或,当a 和 b不同时,则结果为true,否则为false

public class test {
    public static void main(String[] args) {
        int number_first = 10;
        int number_second = 20;
        System.out.println(!(number_first < number_second));  //false
    }
}
public class test {
    public static void main(String[] args) {
        System.out.println((5 > 9) ^ (10 > 8)); //true
    }
}

&&和&使用区别

&&短路与:如果第一个条件为false,则第二个条件不会执行判断,最终结果就是false,效率高

&逻辑与:不管第一个条件是否为false,第二个条件都要判断,效率低

开发中,我们基本使用&&

public class test {
    public static void main(String[] args) {
        int number_first = 10;
        int number_second = 20;
        if (number_first < 5 && ++number_second > 15) {
            System.out.println("条件为真");
        }
        System.out.println("number_first=" + number_first + " number_second=" + number_second); 
        //number_first=10 number_second=20
    }
}

如上图,因为 number_first < 5 为假,所以 ++number_second 就不会执行,如果为逻辑与 & 的话,就会执行 ++number_second了

| 和 || 使用区别

|| 短路或:如果第一个条件为true,则第二个条件不会执行判断,最终结果就为true,效率高

| 逻辑或:不管第一个条件是否为true,第二个条件都要判断,效率低

开发中,我们基本使用 ||

public class test {
    public static void main(String[] args) {
        int number_first = 10;
        int number_second = 20;
        if (number_first < 15 || ++number_second > 30) {
            System.out.println("条件为真");
        }
        System.out.println("number_first=" + number_first + " number_second=" + number_second); 
        //number_first=10 number_second=20
    }
}

如上图,因为 number_first < 15 为真,所以 ++number_second 就不会执行,如果为逻辑或 | 的话,就会执行 ++number_second了

逻辑运算符练习题

注意:x++==6,因为是后++,所以是先判断x==6,再进行自增,x++==6是一个表达式

所以答案如下:6,6  | 6,5 | 11,6 | 11,5

注意:(y=true)是一个赋值语句,&&判断的是赋值完之后的y的值

所以答案为50

4、赋值运算符

赋值运算符就是将某个运算后的值,赋给指定的变量

基本赋值运算符:int a= 10

复合赋值运算符:+=、-=、*=、/=、%=

a += b:[等价a = a + b; ]

a -= b:[等价a = a - b; ]

1> 运算顺序从右往左int num = a + b + c;,先计算a+b+c的值再赋值给num变量

2> 赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值

int num = 20、int num2= 78 * 34 - 10、 int num3 = a

3> 复合赋值运算符等价于下面的效果

比如: a += 3:等价于a = a + 3 其他类推

4> 复合赋值运算符会自动进行类型的强制转换

5、三元运算符

基本语法

条件表达式 ? 表达式1 : 表达式2 ;

运算规则

1、如果条件表达式为true,运算后的结果是表达式1

2、如果条件表达式为false,运算后的结果是表达式2

使用细节

1、表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换)

2、三元运算符可以转成 if else 语句

public class test {
    public static void main(String[] args) {
        int num_first = 1;
        int num_second = 20;
        int result = num_second > num_first ? 13.14 : 5.20 ;  //会报错
        int result = num_second > num_first ? (int)13.14 : (int)5.20 ;  //会损失精度
        double result = num_second > num_first ? 13.14 : 5.20 ;  //nice
        System.out.println(result); 
    }
}

6、运算符优先级

运算符有不同的优先级,所谓优先级就是表达式运算中的运算顺序

如下表,上一行运算符总优先于下一行。只有单目运算符、赋值运算符是从右向左运算的

7、标识符规则与规范

标识符概念:Java对各种变量、方法和类等命名时使用的字符序列称为标识符

凡是自己可以起名字的地方都叫标识符 int number_first = 90 ; 中的num_first就是标识符 

标识符的命名规则(必须遵守)

1.由26个英文字母大小写、0-9、_ 或 $ 组成

2、数字不可以开头 (int 3ab = 1; 是错误)

3、不可以使用关键字和保留字,但能包含关键字和保留字

4、Java中严格区分大小写,长度无限制。int abc 与 int Abc 是两个变量

5、标识符不能包含空格。(int a b = 90; 是错误的)

标识符的命名规范(更加专业)

1、包名:多单词组成时所有字母都小写: aaa.bbb.ccc 比如com.zrh.crm

2、类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz【大驼峰】比如:TankShotGame

3、变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写: xxxYyyZz 【小驼峰,简称驼峰法】比如:tankShotGame

4、常量名:所有字母都大写,多单词时每个单词用下划线连接:XXX_YYY_ZZZ 比如:定义一个所得税率TAX_RATE

8、关键字与保留字

关键字:被Java语言赋予了特殊含义,用做专门用途的字符串(单词),特点:关键字中所有字母都为小写

Java保留字:现有Java版本尚未使用,但以后版本可能会作为关键字使用

自己命名标识符时要避免使用这些保留字byValue、cast、future、 generic、 inner、 operator、 outer、 rest、var 、 goto 、const

六、键盘输入

import java.util.Scanner;  //1、导入java.util包下的Scanner类
public class input {
    public static void main(String[] args) {
        Scanner myScanner = new Scanner(System.in);  //2、创建Scanner对象,new=创建一个对象
        System.out.print("请输入您的姓名:");  //3、使用相关的方法接收用户的输入
        String name = myScanner.next(); //接收用户输入的字符串,当程序执行到next时会等待用户输入
        System.out.print("请输入您的年龄:");
        int age = myScanner.nextInt(); //接收用户输入的int
        System.out.print("请输入您的薪水:");
        double salary = myScanner.nextDouble();  //接收用户输入的double
        System.out.print("您的信息如下:\n姓名:" + name +"\t年龄:" + 
            age + "\t薪水:" + salary );  //打印输入的信息
    }
}

七、进制

对于整数,有四种表示方式:

二进制: 0,1,满2进1,以0b或0B开头

十进制:0-9,满10进1

八进制:0-7,满8进1,以数字0开头表示。

十六进制:0-9及A(10)-F(15),满16进1,以0x或0X开头表示, 此处的AF不区分大小写。

public class test {
    public static void main(String[] args) {
        int numBinary = 0b11001010;  //二进制
        int numOctal = 0571;  //八进制
        int numHex = 0xD5;    //十六进制
        System.out.print("numBinary =" + numBinary + "\tnumOctal=" + numBinary +
            "\tnumHex=" + numHex);
        //numBinary =202  numOctal=202    numHex=213
    }
}

1、进制转换

二进制转十进制规则:从最低位(右边)开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和

0b1011=1*2^0+1*2^1+0*2^2+1*2^3=11

八进制转十进制:从最低位(右边)开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和

0234=4*8^0+3*8^1+2*8^2=156

十六进制转十进制规则:从最低位(右边)开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和

 0X23A=10*16^0+3*16^1+2*16^2=570

十进制转二进制规则:将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制

158=158/2=79……0、79/2=39……1、39/2=19……1、19/2=9……1、9/2=4……1、4/2=2……0、2/2=1……0;所以158=ob10011110

十进制转八进制规则:将该数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的八进制

131=131/8=16……3、16/8=2……0;所以131=0203

十进制转十六进制规则:将该数不断除以16,直到商为0为止,然后将每步得到的余数倒过来,就是对应的十六进制

237=237/16=14……13;所以237=0XED

二进制转八进制规则:从低位开始,将二进制数每三位一组,转成对应的八进制数即可

0b11010110=011、010、110=3、2、6=0326

二进制转十六进制规则:从低位开始,将二进制数每四位一组,转成对应的十六进制数即可

0b11010110=1101、0110=13、6=0XD6

八进制转二进制规则:将八进制数每1位,转成对应的一个3位的二进制数即可

0237=2、3、7=010、011、111=ob10011111

十六进制转二进制规则:将十六进制数每1位,转成对应的4位的一个二进制数即可

0X23B=2、3、B=0010、0011、1011=ob1000111011

2、原码 反码 补码

对于有符号的而言:

1> 二进制的最高位是符号位:0表示正数、1表示负数

2> 正数的原码,反码,补码都一样(三码合一)

3> 负数的反码=它的原码符号位不变,其它位取反(0变1、1变0)

4> 负数的补码=它的反码+1,负数的反码=负数的补码-1

5> 0的反码,补码都是0

6>  java没有无符号数,换言之,java中的数都是有符号的

7> 在计算机运算的时候,都是以补码的方式来运算的

8> 当我们看运算结果的时候,要看他的原码

#例如:int 2
int 2 的源码=00000000 00000000 00000000 00000010
因为2是正数,所以2的原码、补码、反码都一样,三码合一

#例如:int -2
int -2的源码=10000000 00000000 00000000 00000010
int -2的反码=11111111 11111111 11111111 11111101
int -2的补码=11111111 11111111 11111111 11111110

3、位运算符

java中有7个位运算:&、|、^、~、>>、<<、>>>

按位与 &:两位全为1,结果为1,否则为0

按位或 |:两位有一个为1,结果为1,否则为0

按位异或 ^:两位一个为0,一个为1,结果为1,否则为0

按位取反 ~:0变1 ,1变0

算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位

算术左移<<:符号位不变,低位补0

逻辑右移>>>:也叫无符号右移,低位溢出,高位补0

public class test {
    public static void main(String[] args) {
        //在计算机运算的时候,都是以补码的方式来运算的
        //当我们看运算结果的时候,要看他的原码
  
        //2&3  
        //1、2的原码:00000000 00000000 00000000 00000010
        //2、因为2是正数,所以三码合一
        //3、3的原码:00000000 00000000 00000000 00000011
        //4、因为3是正数,所以三码合一
        //&表示两位全为1,结果为1,否则为0
        //所以 2&3的补码为:00000000 00000000 00000000 00000010  //2
        //因为2&3的补码符号位为0,是正数,正数三码合一,所以原码就为上述补码
        System.out.println(2 & 3); //结果为:2
        
        //~-2
        //1、-2的原码:10000000 00000000 00000000 00000010
        //2、-2为负数,负数要得到补码首先需要得到反码(负数的反码=原码符号位不变,其它位取反)
        //-2的反码:11111111 11111111 11111111 11111101
        //3、-2的补码=负数的补码=它的反码+1
        //-2的补码:11111111 11111111 11111111 11111110
        //4、~-2的补码:00000000 00000000 00000000 00000001  //1
        //5、-2的补码符号位为0,是正数,所以三码合一,所以原码也为上述补码
        System.out.println(~-2); //结果为:1

        //~2
        ////1、2的原码:00000000 00000000 00000000 00000010
        ///2、2为正数,所以三码合一
        ///2的补码:00000000 00000000 00000000 00000010
        ///3、~2的补码:11111111 11111111 11111111 11111101
        ///4、~2的补码符号位为1,是负数,所以不能三码合一,需要从补码推导致原码
        ///5、负数的反码=负数的补码-1
        ///~2的反码:11111111 11111111 11111111 11111100
        ///6、负数的原码=符号位不变,反码其他位再取返
        ///~2的原码:10000000 00000000 00000000 00000011  //-3
        System.out.println(~2); //结果为:-3
    }
}
public class test {
    public static void main(String[] args) {
        // 1 >> 2 算数右移
        // 1、1的原码=00000000 00000000 00000000 00000001
        // 1为正数,三码合一
        // 1的补码:00000000 00000000 00000000 00000001
        // 2、>>2表示低位溢出2位,符号位不变,并用符号位补溢出的高位
        // 即最低位的01被去除,最前面就空了两位,空的两位用符号位补全
        // 00000000 00000000 00000000 00000000  //0
        System.out.println(1 >> 2); //结果为:0

        //1 << 2 算术左移
        // 1、1的原码=00000000 00000000 00000000 00000001
        // 1为正数,三码合一
        // 1的补码:00000000 00000000 00000000 00000001
        // 2、<<2表示符号位不变,低位补2个0
        // 00000000 00000000 00000000 00000100  //4
        System.out.println(1 << 2); //结果为:4
    }
}

所以: 1 >> 2 的本质就是 1 / 2 / 2 = 0 、1 << 2 的本质就是 1 * 2 * 2 = 4

八、程序流程控制

在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句:顺序控制、分支控制、循环控制

顺序控制:程序从上到下逐行地执行,中间没有任何判断和跳转

1、分支控制

让程序有选择的的执行,分支控制有三种

//1、单分支if
public class HelloWorld { 
    public static void main(String[] args) { 
        int age = 20;
        if (age > 18){
        System.out.println("你的年龄大于18,请为自己的行为负责!");
        }
    }
}
//2、双分支if-else
public class test {
    public static void main(String[] args) {
    int yearNum = 2020;
    if ((yearNum % 4 == 0 && yearNum % 100 != 0) || yearNum % 400 == 0){
        System.out.print("此年份是闰年");} 
    else {
        System.out.print("此年份不是闰年");
    }
  }
}
//3、多分支if-else if -...-else
public class test {
    public static void main(String[] args) {
    int grade = 95;
    if (grade >= 100){
        System.out.print("信用极好");
    } else if (80 < grade && grade <= 99) {
        System.out.print("信用优秀");
    } else if (60 <= grade && grade<= 80) {
        System.out.print("信用一般");
    } else {
        System.out.print("信用不及格");
    }
  }
}

1> 多分支可以没有else,如果所有的条件表达式都不成立,则一个执行入口都没有

2> 如果有else ,如果所有的条件表达式都不成立,则默认执行else代码块

2、嵌套分支

在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分支外面的分支结构称为外层分支,建议不要超过3层,可读性不好

public class test {
    public static void main(String[] args) {
    double score = 9.5;
    if (score > 8.0) {
        char gender = '女'; 
        //myScanner.next().charAt(0)本质是将字符串的第一个字符转成char
        if (gender == '男') {
            System.out.print("恭喜你进入男子组决赛");
        } else if (gender == '女') {
            System.out.print("恭喜你进入女子组决赛");
        } else {
            System.out.print("性别错误,无法参加决赛");
        }
    } else {
        System.out.println("Sorry,你已被淘汰");
    }
  }
}

3、switch分支结构

import java.util.*;
public class test {
    public static void main(String[] args) {
        Scanner myScanner = new Scanner(System.in);
        System.out.print("请输入字符:");
        char val = myScanner.next().charAt(0);  //在java中只要是有值返回的都可以称为表达式
        switch(val){
            case 'a':
                System.out.println("星期一");
                //break;
            case 'b':
                System.out.println("星期二");
                //break;
            case 'c':
                System.out.println("星期三");
                //break;
            case 'd':
                System.out.println("星期四");
                //break;
            case 'e':
                System.out.println("星期五");
                //break;
            default:
                System.out.println("周末时间");
        }
    }
}

1> switch关键字,表示swtich分支

2> 表达式对应一个值

3> case常量1:当表达式的值等于常量1,就执行语句块1

4> break 表示退出swtich

5> 如果和case常量1匹配,就执行语句块1,如果没有匹配,就继续匹配case常量2

6> 如果一个都没有匹配上,执行default

switch结构细节

1> 表达式数据类型,应和case后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,常量是int就可以,因为char可以自动转为int

2> switch中的表达式返回值必须是:(byte、short、int、char、enum[枚举]、String)

public class test {
    public static void main(String[] args) {
    double c = 1.1;
    switch(c){ //错误,后面类型不符合规定
    case 1.1: //错误,后面类型不符合规定
    System.out.println( "ok3");
    break;
    }
  }
}

3> case子句中的值必须是常量或常量表达式,而不能是变量

4> default子句是可选的,当没有匹配的case时,执行default

5> break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有写break,程序会顺序执行到switch结尾,除非遇到break;

//根据用于指定月份,打印该月份所属的季节
//3.4.5春季、6.7.8夏季、9.10.11秋季、12.1.2冬季
//[必须使用Switch且必须使用穿透]
public class test {
    public static void main(String[] args) {
    int c = 8;
    switch(c){
    case 3:
    case 4:
    case 5:
        System.out.print("春季");
        break;
    case 6:
    case 7:
    case 8:
        System.out.print("夏季");
        break;
    case 9:
    case 10:
    case 11:
        System.out.print("秋季");
        break;
    case 12:
    case 1:
    case 2:
        System.out.print("冬季");
        break;
    default:
        System.out.print("输入月份有误");
    }
  }
}

 switch 和 if 的选择

1> 如果判断的具体数值不多,而且符合byte、short 、int、char、enum[枚举]、String这6种类型。虽然两个语句都可以使用,建议使用swtich 语句

2> 其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广

4、循环控制-for循环

1> for 关键字,表示循环控制

2> for有四要素①循环变量初始化②循环条件③循环操作④循环变量迭代

3> 循环操作,这里可以有多条语句,也就是我们要循环执行的代码

4> 如果循环操作(语句)只有一条语句,可以省略 { },建议不要省略

public class test {
    public static void main(String[] args){
        for (int i = 1 ; i < 10 ; i++){ 
        // i < 10 如果改成 true 就会变成死循环,说到底就是一个布尔表达式
            System.out.println("Hello,World" + i );
        }
    }
}

for循环细节

1> 循环条件是返回一个布尔值的表达式

2> for( ; 循环判断条件 ; )中的循环变量初始化和变量迭代可以写到其它地方,但是两边的分号不能省略

#循环变量初始化 如果写到for外面,那么之后还可以使用循环变量的值,如果循环变量写到for循环里,那么就只能在for循环里面使用
public class test {
    public static void main(String[] args){
        int i = 1;   //循环变量初始化写到外面
        for ( ; i < 10 ; ){
            System.out.println("Hello,World" + i );
            i++;
        }
        System.out.print(i);  //for循环外还可以调用i变量
    }
}

循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开

public class test {
    public static void main(String[] args){
        for (int i = 1, j = 0 ; i < 10 ;i += 2, j++ ){
            System.out.println("i=" + i +" j=" + j );
        }
    }
}

 for循环练习

//打印1~100之间所有是9的倍数的整数,统计个数及总和.
public class test {
    public static void main(String[] args) {
        System.out.println("符合条件的整数如下:");
        int i = 1;
        int count = 0;
        int sum = 0;
        for( ; i <= 100 ; i++){
            if(i % 9 == 0){
                count++;
                sum += i;
                System.out.print(i + "\t");
            }
        }
        System.out.print("\n统计个数为:" + count + "\n总和为:" + sum);
    }
}
for( ; ; ;){ }  //表示无限循环,也叫死循环,可以配合break一起使用

5、循环控制-while循环

while循环也有四要素,只不过四要素的位置与for循环不一样而已

细节说明

1> 循环条件是返回一个布尔值的表达式

2> while循环是先判断再执行语句

public class test {
public static void main(String[] args) {
    //打印1-100之间所有能被3整除的数,使用while
    int number = 1;
    while (number <= 100) {
        switch(number % 3){
            case 0:
            System.out.print(number +"\t");
        }
        number++;
    }
  }
}

6、do…while循环

 

1> do while是关键字

2> 也有循环四要素,只是位置不一样

3> 先执行,再判断,也就是说,一定会至少执行一次

4> 最后有一个分号

public class test {
    public static void main(String[] args){
        int i = 1;  //循环变量初始化
        do{
            System.out.println("i = " + i);   //循环代码块
            i++;    //循环变量迭代
        } while (i<10);  //循环条件
    }
}

 

1> 循环条件是返回一个布尔值的表达式

2> do...while循环是先执行,再判断,因此至少执行一次

//打印1—100之间能被5整除但不能被3整除的数
public class test {
    public static void main(String[] args){
        int i = 1;  //循环变量初始化
        do{
            if(i % 5 == 0 && i % 3 != 0){     //循环代码块
                System.out.print(i + "\t"); } //循环代码块
            i++;    //循环变量迭代
        } while (i<=100);  //循环条件
    }
}

 

7、多重循环

多重循环也叫嵌套循环,将一个循环放在另一个循环体内,就形成了嵌套循环。其中for、while、do..while 均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层,否则,代码的可读性很差】

实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的循环

设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次

//打印9*9乘法表
public class test {
    public static void main(String[] args){
        for(int i = 1 ; i <= 9 ; i++){
            for(int j = 1 ; j <= i ; j++){
                System.out.print(j + "*" + i + "=" + (i*j) + " " );}
            System.out.print("\n");
        }
    }
}

//打印空心金字塔
import java.util.*;
public class test {
    public static void main(String[] args){
        Scanner myScanner = new Scanner(System.in);
        System.out.print("请输入打印图案的层数:");
        int layer = myScanner.nextInt();
        for(int i = 1 ; i <= layer ; i++){
            for(int k = 1 ; k <= layer - i ; k++){  //此for循环是为了打印每一行开头的空格
                System.out.print(" ");  
            }
            for(int j = 1 ; j <= 2 * i - 1 ; j++){  //此for循环是为了打印每一行的*
                //上图中的2 * i - 1表示每一行对应打印*和空格的个数
                if((i == layer && j % 2 == 1) || j == 1 || j == 2*i-1){  
                //打印最后一行的*或者打印每一行的第一个和最后一个位置的*
                    System.out.print("*");
                } else System.out.print(" "); 
                //将每一行除了第一个和最后一个以外的所有位置打印成空格
            }
            System.out.println("");  //每打完一层的*和空格就换行
        }
    }
}

//打印菱形
import java.util.*;
public class test {
    public static void main(String[] args){
        Scanner myScanner = new Scanner(System.in);
        System.out.print("请输入打印图案的层数:");
        int layer = myScanner.nextInt() / 2  + 1;
        //打印上半部分
        for(int i = 1 ; i <= layer ; i++){
            for(int k = 1 ; k <= layer - i ; k++){  //此for循环是为了打印每一行开头的空格
                System.out.print(" ");  
            }
            for(int j = 1 ; j <= 2 * i - 1 ; j++){  //此for循环是为了打印每一行的*
                //上图中的2 * i - 1表示每一行对应打印*和空格的个数
                if(j == 1 || j == 2*i-1){  
                //打印最后一行的*或者打印每一行的第一个和最后一个位置的*
                    System.out.print("*");
                } else System.out.print(" "); 
                //将每一行除了第一个和最后一个以外的所有位置打印成空格
            }
            System.out.println("");  //每打完一层的*和空格就换行
        }        
        //打印下半部分
        for(int i = 1 ; i <= layer - 1 ; i++){
            for(int k = 1 ; k <= i ; k++){  //此for循环是为了打印每一行开头的空格
                System.out.print(" ");  
            }
            for(int j = 1 ; j <= (2 * layer - 3) - 2 * (i - 1); j++){  //此for循环是为了打印每一行的*
            //上图中的(2 * layer - 3) - 2 * (i - 1)表示每一行对应打印*和空格的个数
                if(j == 1 || j == (2 * layer - 3) - 2 * (i - 1)){  
            //打印最后一行的*或者打印每一行的第一个和最后一个位置的*
                    System.out.print("*");
                } else System.out.print(" "); 
            //将每一行除了第一个和最后一个以外的所有位置打印成空格
            }
        System.out.println("");  //每打完一层的*和空格就换行
        }
    }
}

8、化繁为简、先死后活

统计3个班成绩情况,每个班有5名同学,求出各个班的平均分和所有班级的平均分,并统计三个班及格人数(学生的成绩从键盘输入)

1> 化繁为简

①要统计三个班的成绩情况,可以先统计一个班的成绩情况,因为每个班有5个学生,每个学生的成绩都是键盘输入,所以可以设置for循环输入5次

②成绩输入完后计算平均分,可以定义一个double sum把5名学生的成绩进行累和,然后除以人数就可以得到一个班平均值

③一个班处理完了,可以处理三个班,一个班的代码框架完成后,可以在代码框架外加一个循环3次的for循环,这样就可以得到3个班的成绩情况

④要计算三个班的平均分,可以定义一个变量double totalScore 来累和每个班成绩,然后除以三个班的人数就是三个班的平均值

⑤统计三个班的及格人数,可以设置一个变量int count,如果输入学生的成绩>=60,那么就对count++,最后打印count即可

2> 先死后活

①上面所说每班5个学生,就做了5次的for循环,可以将5设置成变量

②上面所说3个班级,就做了3次的for循环,可以将3设置成变量

先死后活的含义就是先将内容写死,然后通过变量将内容替换,这样就可以修改变量的内容达到灵活的目的

9、跳转控制语句break

break语句用于终止某个语句块的执行,一般使用在switch或者循环 [ for、while、do - while ] 中

 

public class test {
    public static void main(String[] args) {
        label1:
        for(int i = 1 ; i <= 3 ; i++){
            label2:
            for(int k = 1 ; k <= 3 ; k++){
                    if(i == 2 && k == 2){
                        break; //break label1;
                    }
                System.out.println("i= " + i + " k=" + k);
            }
        }
    }
}

break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块

1> break语句可以指定退出哪层

2> label1是标签,名字由程序员指定

3> break后指定到哪个label就退出到哪里

4> 在实际的开发中,尽量不要使用标签.

5> 如果没有指定break,默认退出最近的循环体

上图代码将break label1取消注释以后,结果如下:

//实现登录验证,有3次机会,如果用户名为"丁真",密码"666"提示登录成功,否则提示还有几次机会,请使用for+break
import java.util.Scanner;
public class test{
    public static void main(String[] args){
        int oppo = 3;
        Scanner myScanner = new Scanner(System.in);
        String username = "";
        String password = "";
        for(int i = 1 ; i <= 3 ; i++){
            System.out.print("请输入用户名:");
            String username = myScanner.next();
            System.out.print("请输入密码:");
            String password = myScanner.next();
            if("丁真".equals(username) && "123".equals(password)){
                System.out.println("登陆成功");
                break;
            } else {
                oppo--;
                System.out.println("登陆失败,剩余" + oppo + "次机会");
            }
        }
    }
}

 注意:判断两个字符安川是否相等需要用到String下的equals方法

"丁真".equals(username)

10、跳转控制语句continue

1> continue语句用于结束本次循环,继续执行下一次循环

2> contimue语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环﹐这个和前面的标签的使用的规则一样

public class test{
    public static void main(String[] args){
        label1:
        for(int i = 1 ; i <= 3 ; i++){
            label2:
            for(int k = 1 ; k <= 3 ; k++){
                if(k == 2){
                    continue;  //等价于continue label2;结果为打印三次k=1、k=2
                    //continue label1;结果为打印三次k=1
                }
            System.out.println("k=" + k);
            }
        }
    }
}

11、跳转控制语句return

return使用在方法上,表示跳出所在的方法,在讲解方法的时候,会详细的介绍,这里我们简单的提一下

注意:如果return 写在 main方法,表示退出程序

public class test{
    public static void main(String[] args){
        int i = 1;
        do{
            if(i == 2){
                return;
            }
            System.out.println("i=" + i);
            i++;
        }while(i <= 3);
        System.out.println("循环结束");  //return之后这句话就不会被执行
    }
}

12、程序控制结构作业

//计算 1-1/2+1/3-1/4......1/100 的和
public class test{
    public static void main(String[] args){
        double sum = 0.0;
        for(int i = 1 ; i <= 100 ; i++){
            if (i % 2 == 0){
                sum -= (1.0 / i);  //注意1.0,注意java中/的陷阱
            }else{
                sum += (1.0 / i);} 
        }
        System.out.println("和=" + sum);
    }
}
//求 1+(1+2)+(1+2+3)+(1+2+3+4)+...+(1+2+...+100)的结果
//第一种
public class test{
    public static void main(String[] args){
        int sum = 0;
        int res = 0;
        for(int i = 1 ; i <= 100 ; i++){
            sum += i;
            res += sum;
        }
        System.out.println("result=" + res);
    }
}
//第二种
public class test{
    public static void main(String[] args){
        int sum = 0;
        for(int i = 1 ; i <= 100 ; i++){
            for(int k = 1 ; k <= i ; k++){
                sum += k;
            }
        }
        System.out.println("result=" + res);
    }
}

九、数组

数组可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型,即:数组就是一组数据

public class test{
public static void main(String[] args){
        int arr[] = new int[5];  
        //int表示数组为int类型
        //arr表示数组的名称
        //[]表示数据
        //new int[5]表示在内存中创建数组,数组可以存放5个元素
        System.out.print(arr.length);//arr.length表示arr数组的长度
        arr[0] = 520;  //给数组的第一个元素赋值,数组的索引是从0开始的
        System.out.print(arr[0]); 
    }
}

1、数组的使用方式

1> 动态初始化,声明创建一步到位

public class test{
public static void main(String[] args){
        int arr[] = new int[5];  //int arr[] 等价于 int[] arr
        //创建了一个数组,名字叫arr,可以存放5个int
    }
}

2> 动态初始化,先声明,后创建

public class test{
public static void main(String[] args){
        //int[] arr;也可以
        int arr[];  //先声明数组 
        arr = new int[5];  //再创建数组
    }
}

3> 静态初始化

使用情况:知道数组有多少元素且知道元素具体的值

public class test{
public static void main(String[] args){
        //int[] arr;也可以
        int arr[] = {1,2,3};
        //以上一句代码相当于
        //int arr[] = new int[3];
        //arr[0] = 1;
        //arr[1] = 2;
        //arr[2] = 3;
    }
}
注意:String strs[ ] = new String[ ]{"a","b","c"};  //这样是正确的
String strs[ ] = new String[ 3 ]{"a","b","c"};  //这样是错误的

2、数组使用注意事项和细节

1> 数组是多个相同类型数据的组合(自动类型转换的也可以放在同一个数组里),实现对这些数据的统一管理

2> 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用

3> 数组创建后,如果没有赋值,有默认值

int 0、short 0、byte 0、long 0、float 0.0、double 0.0、char /u0000、boolean false、String null

4> 使用数组的步骤:①声明数组并开辟空间、②给数组各个元素赋值、③使用数组

5> 数组的下标是从0开始的

6> 数组下标必须在指定范围内使用,否则报:下标越界异常,比如

 7> 数组属引用类型,数组型数据是对象(object)

//创建一个char类型的26个元素的数组,分别放置'A‘-'Z',使用for 循环访问所有元素并打印出来
public class test{
public static void main(String[] args){
        char array[] = new char[26];
        for(int i = 0 ; i <= 25 ; i++){
            array[i] = (char)('A' + i);  
            //array[]是数组类型,array[1]是char类型
            System.out.print(array[i] + " ");
        }
    }
}

3、赋值机制与数组拷贝

在引用传递的赋值机制下,修改array_second元素的值是会影响到array_first元素的值的,因为array_firs和array_second指向的是同一个内存地址

如果想要拷贝一个相互不影响的数组,那么就需要用拷贝的赋值机制对数组进行拷贝,操作如下:

新建一个数组,数组的长度与需要拷贝的数组相同,再将需要拷贝的数组元素的值一 一赋给新建数组的元素的值即可

public class test{
public static void main(String[] args){
    int array_first[] = {1,3,5,7,9};
    int array_second[] = new int[array_first.length];
    for(int i = 0 ; i < array_first.length ; i++){
        array_second[i] = array_first[i];}
    System.out.println("array_first的地址:" + array_first);  //[I@15db9742
    System.out.println("array_second的地址:" + array_second);  //[I@6d06d69c
    }
}

4、数组反转输出

要求:把数组的元素内容反转

//根据规律反转
public class Test { 
    public static void main(String[] args) { 
        int arrary_first[] = {1,2,3,4,5,6};
        int temp = 0;
        for(int i = 0 ; i < arrary_first.length / 2 ; i++){
            temp = arrary_first[i];
            arrary_first[i] = arrary_first[arrary_first.length - i - 1];
            arrary_first[arrary_first.length - i - 1] = temp;
            }
        for(int i = 0 ; i < arrary_first.length ; i++){
            System.out.print(arrary_first[i] + " "); 
            }
    }
}
//使用逆序赋值反转
public class Test { 
    public static void main(String[] args) { 
        int arrary_first[] = {1,2,3,4,5,6};
        int arrary_second[] = new int[arrary_first.length];
        for(int i = arrary_first.length - 1 ; i >= 0 ; i--){
        arrary_second[arrary_first.length - i - 1] = arrary_first[i];
        }
        arrary_first = arrary_second;
        for(int i = 0 ; i < arrary_first.length ; i++){
            System.out.print(arrary_first[i] + " "); 
            }
    }
}

5、数组扩容

//实现动态的给数组添加元素效果,实现对数组扩容
//用户可以通过如下方法来决定是否继续添加:添加成功,是否继续? y/n
import java.util.Scanner;
public class test{
    public static void main(String[] args){
        int array[] = {1,2,3};
        Scanner myScanner = new Scanner(System.in);
        System.out.println("原始数组内容如下:");
        for(int i = 0 ; i < array.length ; i++){
            System.out.print(array[i]+" ");
        }
        System.out.println("");
        do{
            int newarray[] = new int[array.length + 1];
            for(int i = 0 ; i < array.length ; i++){
                newarray[i] = array[i];
            }
            System.out.print("请输入增加的元素:");
            int add = myScanner.nextInt();
            newarray[array.length] = add ;
            array = newarray;
            System.out.print("添加成功,是否继续添加? y/n:");
            char val = myScanner.next().charAt(0);
            if(val != 'y') break;
        } while(true);
        System.out.println("修改后数组内容如下:");
        for(int i = 0 ; i < array.length ; i++){
            System.out.print(array[i]+" ");
        }
    }
}

6、数组缩减

//有一个数组{1,2,3, 4,5},可以将该数组进行缩减,提示用户是否继续缩减,每次缩减最后那个元素。当只剩下最后一个元素:提示不能再缩减
import java.util.Scanner;
public class test{
    public static void main(String[] args){
        int array[] = {1,2,3,4,5};
        Scanner myScanner = new Scanner(System.in);
        System.out.println("原始数组内容如下:");
        for(int i = 0 ; i < array.length ; i++){
            System.out.print(array[i]+" ");
        }
        System.out.print("\n准备就绪,是否开始缩减? y/n:");
            char val_t = myScanner.next().charAt(0);
            if(val_t != 'y') return;
        do{
            int newarray[] = new int[array.length - 1];
            for(int i = 0 ; i < array.length - 1 ; i++){
                newarray[i] = array[i];
            }
            array = newarray;
            System.out.print("缩减成功,是否继续缩减? y/n:");
            char val = myScanner.next().charAt(0);
            if(val != 'y') break;
            if(array.length == 1){
                System.out.println("数组仅剩一个元素,无法继续缩减");
                break;
            }
        } while(true);
        System.out.println("修改后数组内容如下:");
        for(int i = 0 ; i < array.length ; i++){
            System.out.print(array[i]+" ");
        }
    }
}

7、排序

排序是将多个数据,依指定的顺序进行排列的过程

排序的分类:

内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)

外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)

冒泡排序法

冒泡排序(Bubble Soting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始〉,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的气泡一样逐渐向上冒。

将五个无序: 24,69,80,57,13 使用冒泡排序法将其排成一个从小到大的有序数列

public class test{
    public static void main(String[] args){
        int array[] = {24,69,80,57,13};
        int temp = 0;
        int time = 0;
        for(int k = 1 ; k < array.length ; k++){
            time = 0;  //防止已经排好序,所以用来直接终止循环
            for(int i = 0 ; i < array.length - k ; i++){
                if(array[i] > array[i + 1]){
                    temp = array[i];
                    array[i] = array[i + 1];
                    array[i + 1] = temp;
                    time++;  //插个眼
                }
            }
            if(time == 0) break;  //眼==0,就代表顺序已经排好了
        }
        for(int i = 0 ; i < array.length ; i++){
             System.out.print(array[i]+" ");
         }
    }
}

十、二维数组

1> 从定义形式上看:int[ ][ ] name 或者 int name[ ][ ]

2> 二维数组的每一个元素是一个一维数组

二维数组的每个元素是一维数组,所以如果需要得到每个一维数组的值还需要再次遍历

访问第(i+1)个一维数组的第j+1个值:arr[i][j];

arr[ i ]表示二维数组的第i+1个元素比如:arr[0]表示二维数组的第一个元素

arr.length表示二维数组的长度、arr[ i ].length得到对应的每个一维数组的长度

1、二维数组在内存中的存在形式

2、二维数组的使用方式

1> 动态初始化,声明创建一步到位

public class test{
    public static void main(String[] args){
        int array[][] = new int[2][3];
    }
}

2> 动态初始化,先声明,后创建

public class test{
    public static void main(String[] args){
        int array[][];  //先声明
        array = new int[2][3];  //后创建
    }
}

3> 动态初始化,列数不确定

创建二维数组,二维数组中有3个一维数组,但是每个一维数组还没有开辟数据空间

public class test{
    public static void main(String[] args){
        int array[][] = new int[3][];  //没有给二维数组中的一维数组开辟空间
        array[0] = new int[2];
        array[1] = new int[2];
        array[2] = new int[2];
        //以上四句等价于 array[][] = new int[3][2];
    }
}

public class test{
    public static void main(String[] args){
        int array[][] = new int[3][];  //没有创建一维数组元素的个数
        for(int i = 0 ; i < array.length ; i++){
            array[i] = new int[i + 1];  //给一维数组开辟空间
            for(int k = 0 ; k< array[i].length ; k++){
                array[i][k] = i + 1;  //给一维数组的元素赋值
                System.out.print(array[i][k] + " ");
            }
            System.out.println();
        }
    }
}

4> 静态初始化

public class test{
    public static void main(String[] args){
        int array[][] = new {{1,2,3},{1,2},{1}};
        //定义了一个二维数组
        //二维数组有三个元素,每个元素都是一维数组
        //第一个一维数组有3个元素、第二个有2个元素、最后一个有一个元素
    }
}

3、二维数组使用细节和注意事项

1> 一维数组的声明方式有:int[ ] name或者int name[ ]

2> 二维数组的声明方式有:int[ ][ ] name 或者 int [ ] name[ ] 或者int name[ ][ ]

3> 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同

比如:map[ ][ ]是一个二维数组

int map[ ][ ]= {{1,2},{3,4,5}}

map[0]是一个含有两个元素的一维数组,map[1]是一个含有三个元素的一维数组构成,我们也称为列数不等的二维数组

int[ ] x,y[ ] //表示x是int类型一维数组,y是int类型的二维数组

4、二维数组练习

import java.util.Scanner;
public class test{
    public static void main(String[] args){
        Scanner myScanner = new Scanner(System.in);
        System.out.print("请输入打印杨辉三角的行数:");
        int val = myScanner.nextInt();  //接收杨辉三角的行数
        int array[][] = new int[val][];  //未开辟一维数组空间
        for(int i = 0 ; i < array.length ; i++){
            array[i] = new int[i + 1];  //开辟一维数组的空间
            for(int j = 0 ; j < array[i].length ; j++){
                if(j == 0 || j == array[i].length - 1){  //首位和末尾打印1
                    array[i][j] = 1;
                }else{array[i][j] = array[i - 1][j] + array[i - 1][j - 1];}
                System.out.print(array[i][j] + " ");
            }
            System.out.println();
        }
    }
}

//已知有个升序的数组,要求插入一个元素,该数组顺序依然是升序
//比如:[10,12,45,90],添加23后,数组为[10,12,23,45,90]
import java.util.Scanner;
public class test{
    public static void main(String[] args){
        Scanner myScanner = new Scanner(System.in);
        System.out.print("请输入插入的数:");
        int val = myScanner.nextInt();
        int temp = 0;
        int array[] = {10,12,45,90,99,102,129,168};
        int array_temp[] = new int[array.length + 1];
        for(int i = 0 ; i < array.length ; i++){
            if(array[i] <= val){array_temp[i] = array[i];}
            else if(array[i] > val){
                temp++;
                //i + 1的目的是为了将array_temp的i空出来给val
                array_temp[i + 1] = array[i];  
                if(temp == 1){   //temp等于1的时候对应的i就是val的索引
                    array_temp[i] = val;
                }
            }
        }
        //防止val的值比array每一个元素的值都大
        if(array_temp[array.length] == 0){
            array_temp[array.length] = val;
        }
        array = array_temp;
        for(int i = 0 ; i < array.length ; i++){
            System.out.print(array[i] + " ");
        }
    }
}

十一、面向对象编程

1、类与对象

类是抽象的,概念的,代表一类事物,比如人类、猫类、狗类......

类是数据类型;对象是具体的、实际的,代表一个具体事物,即是实例

类是对象的模板,对象是类的一个 个体,对应一个实例

public class test{
    public static void main(String[] args){
        Cat hk = new Cat();  //创建一个对象
        hk.name = "flower";
        hk.age = 2;
        hk.color = "white";
     Cat xk = hk; System.out.print(
"这只猫的信息为:" + hk.name + " " + hk.age + " " + hk.color); } } class Cat{ //定义 Cat 类 String name; int age; String color; }

2、对象在内存中的存在形式

非基本数据类型都存放在方法区的常量池中,基本数据类型就直接保存在类所在的内存地址里

类和对象的内存分配机制,Java内存的结构分析

1> 栈:一般存放基本数据类型(局部变量)

2> 堆:存放对象(Cat hk,数组等)

3> 方法区:常量池(常量,比如字符串),类加载信息

注意:如果xk = null; 这代表xk置空,置空之后xk与创建的对象就失去了链接

3、属性

从概念或叫法上看:成员变量= 属性 = field(字段) (即成员变量是用来表示属性的,本文中统一叫属性)

class Cat{  //定义 Cat 类
    String name; //成员变量、属性、字段
    int age;
    String color;
    String[] hosts; //属性可以是基本数据类型,也可以是引用类型(对象,数组)    
}

注意事项和细节说明

1> 属性的定义语法同变量,示例:访问修饰符  属性类型  属性名;

简单的介绍访问修饰符:控制属性的访问范围

有四种访问修饰符public、proctected、默认、 private

2> 属性的定义类型可以为任意类型,包含基本类型或引用类型

3> 属性如果不赋值,有默认值,规则和数组一致

具体说: int 0、short 0、 byte 0、 long 0、float 0.0、double 0.0、char /u0000、boolean false、String null

public class test{
    public static void main(String[] args){
        Cat hk = new Cat();
        //hk是对象名,也叫对象引用
        //new Cat()为创建的对象空间,是真正的对象,对象名只是指向了这个对象空间而已
    }
}

创建对象及访问属性

创建对象1:先声明,再创建
Cat cat ; //先声明对象cat
cat = new Cat(); //创建
创建对象2:直接创建
Cat cat = new Cat();

//如何访问属性
对象名.属性名;
cat.name;
cat.age;
cat.color;

Java创建对象的流程简单分析

1> 先加载Cat类信息(属性和方法信息,只会加载一次)

2> 在堆中分配空间,进行默认初始化(int 0、double 0.0.....)

3> 把地址赋给hk,hk就指向对象

4> 进行指定初始化,比如 hk.name ="flower" hk.age = 2

4、方法

1> 方法写好后,如果不去调用(使用),不会输出

2> 先创建对象,然后调用方法即可

public class test{
    public static void main(String[] args){
        Person hk = new Person();  //创建一个对象
        hk.speak();  //调用speak方法
    }
}

class Person{  //定义 Cat 类
    String name;
    int age;
    //1、public表示方法公开
    //2、void表示方法没有返回值
    //3、speak()中speak为方法名,()为形参列表
    //4、{ } 方法体,可以写我们要执行的代码体
    public void speak(){
        System.out.print("This is a good man.");
    }
}

方法快速入门

public class test{
    public static void main(String[] args){
        Person hk = new Person();  //创建一个对象
        hk.cal(500); //调用cal方法,同时传入参数,即n=500
        int result = hk.getSum(10,20);
        System.out.println(result);
    }
}
class Person{  //定义 Cat 类
    String name;
    int age;
//定义speak方法 public void speak(){ System.out.println("This is a good man."); } //定义cal方法 public void cal(int n){ //表示当前有一个 形参 n(形式参数),可以接受用户输入 int sum = 0; for(int i = 1 ; i <= n ; i++){ sum += i; } System.out.println("1+2+...+" + n + "=" + sum); } //定义getSum方法 //1、piblic后的int表示方法执行后,返回一个 int 值 //2、(int num1 , int num2)表示形参列表,可以接受用户传入两个参数 //3、return表示将结果返回 public int getSum(int num1 , int num2){ int res = num1 + num2; return res; } }

方法的调用机制

这里使用getSum方法进行演示

 

 方法调用总结

1> 当程序执行到方法时,就会开辟一个独立的空间(栈空间)

2> 当方法执行完毕,或者执行到return语句时,就会返回相应的结果到调用方法的地方

3> 返回后,栈空间被垃圾回收机制回收,然后继续执行方法后面的代码

4> 当main方法(栈)执行完毕,整个程序退出

5、成员方法的定义及细节

1> 形参列表:表示成员方法输入的参数 cal(int n)、getSum(int num1, int num2)

2> 返回数据类型:表示成员方法输出,void表示没有返回值

3> 方法主体:表示为了实现某一功能代码块

4> return语句不是必须的

成员方法的好处:提高代码的复用性、可以将实现的细节封装起来,然后供其他用户来调用即可

方法细节及注意事项

返回数据类型

1> 一个方法最多有一个返回值,如果需要返回多个值可以返回数组

2> 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)

public class test{
    public static void main(String[] args){
        myTools tools = new myTools();  //创建对象
        int[] result = tools.printArr(20,10);
        System.out.println("result[0]=" + result[0]);
        System.out.println("result[1]=" + result[1]);
    }
}

class myTools{
    public int[] printArr(int num1 , int num2){  //返回数据类型为数组
        int[] res = new int[2];   //创建数组
        res[0] = num1 + num2;
        res[1] = num1 - num2;
        return res; //返回数组
    }
}

3> 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值,而且要求返回值类型必须和 return 的值类型一致或兼容

4> 如果方法是void,则方法体中可以没有retun语句,或者只写return ;(即return后没有返回值)

方法名

遵循驼峰命名法,最好见名知义,表达出该功能的意思即可,比如得到两个数的和getSum,开发中按照规范

形参列表

1> 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如:getSum(int n1,int n2)

2> 参数类型可以为任意类型,包含基本类型或引用类型,比如:printArr(int[ ][ ] map)

3> 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数

4> 方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致

方法体

方法体里面写完成功能的具体的语句

可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!

即:方法不能嵌套定义,(只是不能嵌套定义,但是方法体里可以调用其他方法)

6、方法调用细节说明

1> 同一个类中的方法调用:直接调用即可

案例演示:myTools类中的sayOk方法调用printArr方法

public class test{
    public static void main(String[] args){
        myTools tools = new myTools();  //创建对象
        tools.sayOk(520);
    }
}

class myTools{
    //定义printArr方法
    public void printArr(int num){  //无数据类型
        System.out.println("你输入的数字为=" + num);
    }
    //定义sayOk方法
    public void sayOk(int num){
        System.out.println("sayOk方法已调用");
        printArr(num);  //调用同一个类的printArr方法
    }
}

2> 跨类中的方法A类调用B类方法:需要通过对象名调用,比如:对象名.方法名(参数);

案例演示:myTools类 的 sayHello方法 调用 herTools类 的 printArr方法

public class test{
    public static void main(String[] args){
        myTools tools = new myTools();  //创建对象
        tools.sayHello(520);
    }
}

class myTools{

    //定义sayHello方法
    public void sayHello(int num){
        System.out.println("sayHello方法已调用");
        herTools temp = new herTools();
        temp.printArr(num);   //调用不同类的printArr方法
    }
}

class herTools{
    //定义printArr方法
    public void printArr(int num){  //无返回数据类型
        System.out.println("你输入的数字为=" + num);
    }
}

3> 特别说明:跨类的方法调用和方法的访问修饰符相关,后面讲到访问修饰符时再细说

7、成员方法传参机制

1> 基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        int n = 10;
        int m = 20;
        tools.swap(n , m);
        System.out.print("main方法的:n=" + n + " m=" + m);
        //main方法的:n=10 m=20
    }
}
class Mytools{
    public void swap(int n , int m){
        int temp = n;
        n = m;
        m = temp;
        System.out.println("swap方法的:n=" + n + " m=" + m);
        //swap方法的:n=20 m=10
    }
}

2> 引用类型传递的是地址(传递也是值,但值是地址),可以通过形参的改变影响实参

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        int[] array = {10,20,30};
        tools.arrayChange(array);
        System.out.print("main方法:array[0]=" + array[0]);
        //main方法:array[0]=100
    }
}
class Mytools{
    public void arrayChange(int[] array){
        array[0] = 100;    //将数组的第一位改为100
        System.out.println("arrayChange方法:array[0]=" + array[0]);
        //arrayChange方法:array[0]=100
    }
}

8、克隆对象

编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象,即克隆对象

注意:要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同

public class test{
    public static void main(String[] args){
        Person p = new Person();
        p.name = "King";
        p.age = 22;
        Mytools tools = new Mytools();
        Person p2 = tools.copyPerson(p);
        System.out.println("P的属性为:" + "p.name=" + p.name + " p.age=" + p.age);
        System.out.println("P2的属性为:" + "p2.name=" + p2.name + " p2.age=" + p2.age);
        System.out.println(p == p2);  //判断p与p2是否属于同一对象空间
    }
}

class Person{
    String name;
    int age;
}

class Mytools{
    //编写方法的思路
    //1、方法的返回类型 Person,属于一个类(int 是 100 的类)
    //2、方法的名字 copyPerson
    //3、方法的形参是一个对象,对象又是Person类的实例,所以为 Person p
    public Person copyPerson(Person p){
        Person p2 = new Person();
        p2.name = p.name;
        p2.age = p.age;
        return p2;
    }
}

十二、递归

递归就是方法自己调用自己,每次调用时传入不同的变量

public class test{
    public static void main(String[] args){
        Recursion temp = new Recursion();
        temp.printWord(4);  //传入初始参数为4
    }
}
class Recursion{
    public void printWord(int number){
        if(number > 2){
            printWord(number - 1);  //自己调用自己
        }
        System.out.println("number=" + number);
    }
}

递归细节

1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间)

2、方法的局部变量是独立的,不会相互影响,比如上图中的number变量

3、如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据,那么局部变量之间就会受到影响

4、递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError

5、当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕(栈空间也就被回收)

//请使用递归的方式求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出斐波那契的第n个数的值是多少?
//思路分析
//1、当n = 1斐波那契数是1
//2、当n = 2斐波那契数是1
//3、当n>= 3斐波那契数是前两个数的和
//4、这里就是一个递归的思路
//
public class test{
    public static void main(String[] args){
        Recursion temp = new Recursion();
        int res = temp.fobi(7);
        System.out.print(res);
    }
}
class Recursion{
    public int fobi(int n){
        if(n == 1 || n == 2){
            return 1;
        } else { 
            return fobi(n - 1) + fobi(n - 2);
        }
    }
}

//老鼠出迷宫
public class Maze{
    public static void main(String[] args){
        //打印迷宫,0表示可以走,1表示障碍物
        int[][] array = new int[8][7];
        for(int i = 0 ; i < array.length ; i++){
            for(int j = 0 ; j < array[i].length ; j++){
                if(i == 0 || i == array.length - 1){
                    array[i][j] = 1;
                }
            }
        array[i][0] = 1;
        array[i][6] = 1;
        }
        array[3][1] = 1;
        array[3][2] = 1;
        //打印原迷宫
        System.out.println("====原迷宫如下====");
        for(int i = 0 ; i < array.length ; i++){
            for(int j = 0 ; j < array[i].length ; j++){
                System.out.print(array[i][j] + " ");
            }
        System.out.println();
        }
        Mytools temp = new Mytools();  //创建Mytools对象
        temp.findWay(array,1,1);
        //打印迷宫出路
        System.out.println("====迷宫出路如下====");
        for(int i = 0 ; i < array.length ; i++){
            for(int j = 0 ; j < array[i].length ; j++){
                System.out.print(array[i][j] + " ");
            }
        System.out.println();
        }
    }
}

class Mytools{
    //1、findWay方法用来找迷宫的出路,如果找到返回true,否则返回false
    //2、接收的map就是用来表示迷宫的二维数组
    //3、(i,j)用来表示小老鼠的位置,初始位置为(1,1)
    //4、规定递归过程中map数组中元素各个值的含义
    //    0表示可以走、1表示障碍物、2表示可以走、3表示走过,但是此路不通
    //5、当map[6][5] == 2 就说明找到了通路,否则就继续找
    public boolean findWay(int[][] map , int i , int j){
        if(map[6][5] == 2){ //说明找到了通路
            return true;
        } else {
            if(map[i][j] == 0){ //当前位置等于0,说明可以走
                map[i][j] = 2; //如果此路可以走通,那么2就是我们的轨迹
                //使用找路策略查看此路是否真的可以走通
                ////找路策略:下---右---上---左
                if(findWay(map , i + 1 , j)){ //先向下走
                    return true;
                } else if(findWay(map , i , j + 1)){ //再向右走
                    return true;
                } else if(findWay(map , i - 1 , j)){ //再向上走
                    return true;
                } else if(findWay(map , i , j - 1)){ //再向左走
                    return true;
                } else{  //上下左右都走不通
                    map[i][j] = 3;
                    return false;
                }
            } else return false;  //map[i][j] == 1或者2或者3都返回false
        }
    }
}

十三、方法的重载

java 中允许在同一个类中,定义多个同名的方法,但要求形参列表不一致

重载的好处:1、减轻了起名的麻烦  2、减轻了记名的麻烦

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        System.out.println(tools.m(5));
        System.out.println(tools.m(10,50));
        tools.m("HK9527");
    }
}

class Mytools{
    public int m(int num){
        return num * num;
    }
    public int m(int num1 , int num2){
        return num1 * num2;
    }
    public void m(String str){
        System.out.print(str);
    }
}

注意事项和使用细节

1、方法名必须相同

2、形参列表必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)

3、返回类型无要求,及相同与否都可以

4、在调用重载方法时,优先调用传入参数与方法定义参数的类型未发生类型转换的方法

十四、可变参数

java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,就可以通过可变参数实现

访问修饰符  返回类型  方法名(数据类型...形参名){

}

1、可变参数的实参可以为0个或任意多个

2、可变参数的实参可以为数组

3、可变参数的本质就是数组

4、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

5、一个形参列表中只能出现一个可变参数

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        tools.showScore("HK9527",85,95,87,96,88);
    }
}

class Mytools{
    public void showScore(String name , double... score){
        double sum = 0;
        for(int i = 0 ; i < score.length ; i++){  //score.length就可以表现可变参数的本质为数组
            sum += score[i];
        }
        System.out.println("姓名:" + name);
        System.out.println("总成绩:" + sum);
    
    }
}

十五、作用域

1、在java编程中,主要的变量就是属性(成员变量)和局部变量

2、我们说的局部变量一般是指在成员方法中定义的变量

3、java中作用域的分类

全局变量:也就是属性,作用域为整个类体

局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中

4、全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值

注意事项和细节使用

1、属性和局部变量可以重名,访问时遵循就近原则

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        tools.var();
    }
}

class Mytools{
    String name = "ZRH";  //属性/全局变量
    public void var(){
        String name = "KING";  //局部变量
        System.out.println(name)  //输出结果:KING
    }
}

2、在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名

3、属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁

局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。

4、作用域范围不同

全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)

局部变量:只能在本类对应的方法中使用

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools();
        tools.var();
        Histools htools = new Histools();
        htools.temp(tools);  //其他类通过传入对象参数调用
    }
}

class Mytools{
    String name = "ZRH";  //属性/全局变量
    public void var(){
        int age = 10;  //局部变量
        System.out.println(name);  //在本类的方法中使用全局变量
        System.out.println(age);   //只能在本类对应的方法中使用
    }
}
class Hertools{
    public void temp(){
    Mytools value = new Mytools();
    System.out.println(value.name); //其他类通过创建对象调用
    }
}
class Histools{
    public void temp(Mytools tools){
    System.out.println(tools.name); //其他类通过传入对象参数调用
    }
}

5、修饰符不同

全局变量/属性可以加修饰符,局部变量不可以加修饰符

class Mytools{
    public String name = "ZRH";  //这样写是正确的,属性/全局变量可以加修饰符
    public void var(){
        public int age = 10;  //这样写是错误的,局部变量不可以加修饰符,会报错
    }
}

十六、构造器

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化

[修饰符] 方法名 (形参列表) {
    方法体;
}

它有几个特点:

1> 方法名和类名相同

2> 没有返回值

3> 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化

4> 参数列表和成员方法一样的规则

5> 构造器的调用,由系统完成

6> 构造器的修饰符可以默认,也可以是public、protected、private

public class test{
    public static void main(String[] args){
        Mytools tools = new Mytools("Zrh");
        System.out.println(tools.name);    //打印结果:Zrh
    }
}

class Mytools{
    String name;  //属性/全局变量
    Mytools(String test_name){
        name = test_name;
    }
}

1、注意事项和使用细节

1> 一个类可以定义多个不同的构造器,即构造器重载

比如:我们可以再给Mytools类定义一个构造器,用来创建对象的时候只指定人名,不需要指定年龄

public class test{
    public static void main(String[] args){
        Mytools first_tools = new Mytools("Zrh");//只指定姓名
        System.out.println(first_tools.name);    //打印结果:Zrh
        Mytools second_tools = new Mytools("Zrh" , 22);//指定姓名和年龄
        System.out.println(second_tools.name + " " + second_tools.age); //打印结果:Zrh 22
    }
}

class Mytools{
    String name;  //属性/全局变量
    int age;
    Mytools(String test_name){
        name = test_name;
    }
    Mytools(String test_name , int test_age){  //构造器重载
        name = test_name;
        age = test_age;
    }
}

2> 构造器名和类名要相同

3> 构造器没有返回值

4> 构造器是完成对象的初始化,并不是创建对象

5> 在创建对象时,系统自动调用该类的构造方法

6> 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),使用javap指令反编译可以查看

 7> 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下(这点很重要)

public class test{
    public static void main(String[] args){
        Mytools first_tools = new Mytools("Zrh");//只指定姓名
        Mytools second_tools = new Mytools();//只有显示定义了无参构造器才可以继续这样使用
    }
}

class Mytools{
    String name;  //属性/全局变量
    int age;
    Mytools(String test_name){
        name = test_name;
    }
    Mytools(){  //无参构造器
    }
}

2、创建对象流程分析

class Person{//类Person
    int age=90;
    String name;
Person(String n,int a){    //构造器
name=n;  //给属性赋值
age=a;  //
    }
}
Person p=new Person("小倩",20);

3、流程分析(面试题)

1> 加载Person类信息(Person.class),只会加载一次

2> 在堆中分配空间(地址)

3> 完成对象初始化

①默认初始化age=0 name=null

②显式初始化age=90,name=null

③构造器的初始化 age =20, name=小倩

4> 在对象在堆中的地址,返回给p(p是对象名,也可以理解成是对象的引用)

十七、this

java虚拟机会给每个对象分配 this用来代表当前对象

1、this的注意事项和使用细节

1> this关键字可以用来访问本类的属性、方法、构造器

2> this用于区分当前类的属性和局部变量

3> 访问成员方法的语法: this.方法名(参数列表)

4> 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器且必须放在第一条语句)

public class test {
    public static void main(String[] args){
        Mytools tools = new Mytools();
    }
}
class Mytools{
    String name;
    int age;
    Mytools(){
        this("Zrh" , 22);  //访问构造器语法必须放置在第一条语句
        System.out.println("Mytools()构造器被调用");
    }
    Mytools(String name , int age){
        System.out.println("Mytools(String name , int age)构造器被调用");
    }
}

5> this 不能在类定义的外部使用,只能在类定义的方法中使用

6> this简单的说,哪个对象调用,this就代表哪个对象

public class test {
    public static void main(String[] args){
        Mytools tools = new Mytools();
        tools.print("Zrh" , 22);
        System.out.println(tools.name + " " + tools.age);
    }
}
class Mytools{
    String name;
    int age;
    public void print(String name , int age){
        this.name = name;    //this表示当前对象tools
        this.age = age;
    }
}

2、匿名对象

匿名对象就是没有名字的对象,使用一次即会销毁

public class temp{
    int count = 9;
    public void count1(){
        count = 10;
    }
    public static void main(String[] args){
        new temp().count1(); //匿名对象存在于堆中,但是只能用一次
    }
}

3、练习题

设计类的成员变量,成员方法,可以电脑猜拳,电脑每次都会随机生成0、1、2

0表示石头、1表示剪刀、2表示布,并要可以显示输赢次数

import java.util.Scanner;
class game{
    public void finger(){
        Scanner Myscanner = new Scanner(System.in);
        int i = 0;
        int j = 0;
        do{
            System.out.println("请输入你的猜拳:(0=石头、1=剪刀、2=布、3=退出)");
            int res = Myscanner.nextInt();
            int temp = (int) (Math.random() * 3);
            if(res == 3) break;
            if (temp - 1 == res || res - 2 == temp) i++;
            else j++;
            System.out.println("电脑随机猜拳:" + temp + "\n你胜利的次数:" + i + "\t你失败的次数:" + j );
        }while (true);
    }
}

十八、IDEA的使用

1、编码设置

2、文件保存内容介绍

3、自动导入所需要的类

 

 4、配置运行快捷键

 5、生成构造器

6、查看一个类的层级关系ctrl +H

7、将光标放在一个方法上,输入ctrl +B,可以定位到方法的位置

8、创建对象时自动分配变量名

import java.util.Scanner;
public class test{
    int age;
    String name;
    public static void main(String[] args){
        //new Scanner(System.in).var  然后回车
        Scanner scanner = new Scanner(System.in);
    }
}

9、模板

10、去掉形参提示符

十九、包

包的本质实际上就是创建不同的文件夹/目录来保存类文件

包的三大作用

1、区分相同名字的类

2、当类很多时,可以很好的管理类

3、控制访问范围

 快速入门

包的命名规则

只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字

命名规范

一般是小写字母+小圆点一般是 com.公司名.项目名.业务模块名

举例:
com.sina.crm.user //用户模块
com.sina.crm.order//订单模块
com.sina.crm.utils //工具类

注意事项和使用细节

1、package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句package

2、 import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求

二十、访问修饰符

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问(范围)

1、公开级别:用public修饰,对外公开

2、受保护级别:用protected修饰,对子类和同一个包中的类公开

3、默认级别:没有修饰符号,向同一个包的类公开

4、私有级别:用private修饰,只有类本身可以访问,不对外公开

四种访问修饰符的访问范围

注意:子类可以访问父类的默认方法/属性的前提是在同一个包中

package com.first;
public class A {
    public int num1 = 10;
    protected int num2 = 20;
    int num3 = 30;
    private int num4 = 40;
    public void Am() {
        System.out.println(num1 + " " + num2 + " " + num3 + " " + num4);
    }
}

package com.first;
public class B {
    public void Bm(){
        A a = new A();
        System.out.println(a.num1 + " " + a.num2 + " " + a.num3);
    }
}

package com.second;
public class A {
    public void An(){
        com.first.A a = new com.first.A();
        System.out.println(a.num1);
    }
}

注意事项

1、修饰符可以用来修饰类中的属性,成员方法以及类

2、只有默认的和public才能修饰类!,并且遵循上述访问权限的特点

3、成员方法的访问规则和属性完全一样

二十一、封装介绍

封装(encapsulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作 [方法],才能对数据进行操作

1、封装的理解和好处

隐藏实现细节

可以对数据进行验证,保证安全合理

2、封装的实现步骤

将属性进行私有化private【使用户不能直接修改属性】

提供一个公共的(public) set方法,用于对属性判断并赋值

public void setXxx(类型参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}

3、提供一个公共的(public) get方法,用于获取属性的值

public 数据类型 getXxx(){
//可以加一些权限判断
return xx;
}

4、案例

请大家看一个小程序,不能随便查看人的姓名,年龄,工资等隐私,并对设置的年龄进行合理的验证

年龄合理就设置,否则给默认年龄18,必须在1-120

年龄,工资不能直接查看,name的长度在2-6字符之间

package com.encapsulation;
public class Show {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("HK9527");
        person.setAge(22);
        person.setSalary(9999.99);
        System.out.println(person.name);  //这样会报错的!
        System.out.println(person.getName() + " " + person.getAge() + " " + person.getSalary());
    }
}
class Person{
    private String name;
    private int age;
    private double salary;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        if(name.length() >= 2 &&name.length() <= 6){
            this.name = name;
        } else {
            System.out.println("输入的名称长度有误");
            this.name = "No One";
        }
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if(age >= 0 && age <= 120){
            this.age = age;
        } else {
            System.out.println("输入的年龄范围不合法");
            this.age = -1;
        }
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        //在这可以加一些权限验证之类的
        this.salary = salary;
    }
}

5、快速设置set、get方法 

6、封装与构造器

package com.encapsulation;
public class Show {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("HK9527");
        person.setAge(22);
        person.setSalary(9999.99);
        System.out.println(person.getName() + " " + person.getAge() + " " + person.getSalary());
        Person person1 = new Person("ZRH",888,8888);
        System.out.println(person1.getName() + " " + person1.getAge() + " " + person1.getSalary());
    }
}
class Person{
    private String name;
    private int age;
    private double salary;
    //构造器与封装相结合
    public Person(){
    }
    public Person(String name , int age , double salary){ 
        setName(name);  //传入参数后继续调用set方法即可
        setAge(age);
        setSalary(salary);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        if(name.length() >= 2 &&name.length() <= 6){
            this.name = name;
        } else {
            System.out.println("输入的名称长度有误");
            this.name = "No One";
        }
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if(age >= 0 && age <= 120){
            this.age = age;
        } else {
            System.out.println("输入的年龄范围不合法");
            this.age = -1;
        }
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        //在这可以加一些权限验证之类的
        this.salary = salary;
    }
}

二十二、继承

继承可以解决代码复用,让我们的编程更加靠近人类思维

当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends来声明继承父类即可

package com.inherit;
public class A {
    String name;
    int age;
    double salary;
    public void info(){
        System.out.println(name + " " + age + " " + salary);
    }
    public void cash(){
        System.out.println(name + "涨工资了");
    }
}

package com.inherit;
public class B extends A{
    public void test_b(){
        System.out.println("小学生" + name + "正在考试");
    }
}

package com.inherit;
public class C extends A {
    public void test_c(){
        System.out.println("大学生" + name + "正在考试");
    }
}


package com.inherit;
public class temp {
    public static void main(String[] args) {
        B b = new B();
        b.name = "银角大王";  //A类的共有属性name
        b.age = 20;          //A类的共有属性age
        b.salary = 5000;     //A类的共有属性salary
        b.info();            //A类的共有方法info
        b.test_b();          //B类的特有方法test_b
        b.cash();            //A类的共有方法cash
        C c = new C();
        c.name = "金角大王";  //A类的共有属性name
        c.age = 30;         //A类的共有属性age
        c.salary = 8000;    //A类的共有属性salary
        c.info();           //A类的共有方法info
        c.test_c();         //C类的特有方法test_c
        c.cash();           //A类的共有方法cash
    }
}

1、继承细节问题

1> 子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问

package com.details;
public class father {
    public String name;
    protected int age;
    double height;
    private double weight;
    public void f1(){
        System.out.println("public f1()方法被调用");
    }
    protected void f2(){
        System.out.println("protected f2()方法被调用");
    }
    void f3(){
        System.out.println("f3()方法被调用");
    }
    private void f4(){
        System.out.println("private f4()方法被调用");
    }
    //定义访问 private double weight; 的public方法
    public void setWeight(double weight){
        this.weight = weight;
    }
    public double getWeight(){
        return weight;
    }
    //定义调用 private void f4() 的public方法
    public void useF4(){
        f4();
    }
}

package com.details;
public class son {
    public static void main(String[] args) {
        father father = new father();
        father.name = "King";
        father.age = 20;
        father.height = 182.5;
        //father.weight;   这样是访问不了的
        father.setWeight(65.5);
        System.out.println(father.getWeight());  //这样可以访问
        father.f1();
        father.f2();
        father.f3();
        //father.f4();  这样是访问不了的
        father.useF4();  //这样可以访问
    }
}

2> 子类必须调用父类的构造器,完成父类的初始化

package com.details;
public class father {
    father(){
        System.out.println("father()构造器被调用");
    }
}

package com.details;
public class son extends father{
    son(){
        //这里存在super();
        System.out.println("son()构造器被调用");
    }
}


package com.details;
public class test {
    public static void main(String[] args) {
        son son = new son();
        //运行结果:
        //father()构造器被调用
        //son()构造器被调用
    }
}

3> 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器

如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过

package com.details;
public class father {
    //没有指定无参构造器,如果定义了构造器,那么原来的默认无参构造器就会失效,除非显式的定义一下
    father(String name , int age){
        System.out.println("father(String name , int age)构造器被调用");
    }
}

package com.details;
public class son extends father{
    son(String name , int age){
        super("tom" , 20);  //在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作
        System.out.println("son(String name , int age)构造器被调用");
    }
}

package com.details;
public class test {
    public static void main(String[] args) {
        son son = new son("name" , 20);
        //运行结果:
        //father(String name , int age)构造器被调用
        //son(String name , int age)构造器被调用
    }
}

4> 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)

package com.details;
public class father {
    //在父类中定义了三个构造器
    father(String name , int age){
        System.out.println("father(String name , int age)构造器被调用");
    }
    father(){
        System.out.println("father()构造器被调用");
    }
    father(String name){
        System.out.println("father(String name)构造器被调用");
    }
}

package com.details;
public class son extends father{
    //在子类中定义三个构造器,每个构造器都可以用super去指定不同父类的构造器
    son(String name , int age){
        super("tom" , 20);
        System.out.println("son(String name , int age)构造器被调用");
    }
    son(String name){
        super("tom");
        System.out.println("son(String name)构造器被调用");
    }
    son(){
        super();
        System.out.println("son()构造器被调用");
    }
}

package com.details;
public class test {
    public static void main(String[] args) {
        son son = new son("name" , 20);
        //运行结果:
        //father(String name , int age)构造器被调用
        //son(String name , int age)构造器被调用
    }
}

5> super在使用时,必须放在构造器第一行(super只能在构造器中使用,不能再成员方法中使用)

6> super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

7> java所有类都是Object类的子类,Object是所有类的父类

8> 父类构造器的调用不限于直接父类!将一直往上追溯直到object类(顶级父类)

9> 子类最多只能继承一个父类(指直接继承),即 java中是单继承机制

思考:如何让A类继承B类和C类?

【A继承B,B继承C】

10> 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

 

2、继承原理的本质分析

查找时要按照查找关系来返回信息

1> 首先看子类是否有该属性,如果子类有这个属性,并且可以访问,则返回信息

2> 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)

3> 如果父类没有就按照(2)的规则,继续找上级父类,直到Object

3、继承课堂练习

编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息

编写PC子类,继承Computer类,添加特有属性【品牌 brand】

编写exercise类,在main方法中创建PC对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息

package com.homework;
public class computer {
    private String Cpu;
    private String Memory;
    private String Disk;
    public computer(String cpu, String memory, String disk) {
        this.Cpu = cpu;
        this.Memory = memory;
        this.Disk = disk;
    }
    public String getDetails(){
        return "Cpu=" + Cpu + " Memory=" + Memory + " Disk=" + Disk;
    }
    public String getCpu() {
        return Cpu;
    }
    public void setCpu(String cpu) {
        Cpu = cpu;
    }
    public String getMemory() {
        return Memory;
    }
    public void setMemory(String memory) {
        Memory = memory;
    }
    public String getDisk() {
        return Disk;
    }
    public void setDisk(String disk) {
        Disk = disk;
    }
}

package com.homework;
public class Pc extends computer{
    private String brand;
    public Pc(String cpu, String memory, String disk, String brand) {
        //这里体现了继承设计的基本思想
        super(cpu, memory, disk);  //父类的构造器完成父类属性初始化
        this.brand = brand;        //子类的构造器完成子类属性初始化
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void info(){
        System.out.println(getDetails() + " Brand=" + brand);
    }
}

package com.homework;
public class exercise {
    public static void main(String[] args) {
        Pc pc = new Pc("i7","32GB","2T SSD","lenovo");
        pc.info();
    }
}

二十三、super关键字

super代表父类的引用,用于访问父类的属性、方法、构造器

1> 访问父类的属性,但不能访问父类的private属性

2> 访问父类的方法,不能访问父类的private方法

3> 访问父类的构造器,只能放在构造器里,且为第一句,只能出现一句!

package com.super_;
public class father {
    public int age = 10;
    protected int height = 175;
    int salary = 8000;
    private int weight = 65;
    father(){
        System.out.println("father()构造器被调用");
    }
    father(int age){
        System.out.println("father(int age)构造器被调用");
    }
    public void f1(){
        System.out.println("public void f1() 方法被调用");
    }
    private void f2(){
        System.out.println("private void f2() 方法被调用");
    }
}

package com.super_;
public class son extends father{
    son(){
        //访问父类构造器的语法:super(参数列表);
        super(20);  //只能放在构造器里,且为第一句,只能出现一句!
        System.out.println("son()构造器被调用");
    }
    public void s1(){
        int age = super.age;  //访问属性的语法:super.属性名;
        int height = super.height;
        int salary = super.salary;
        //super.weight 是私有属性,所以访问不了
        System.out.println(age + " " + height + " " + salary);
    }
    public void s2(){
        super.f1();  //访问方法的语法:super.方法名(参数列表);
        //super.f2()是私有方法,所以访问不了
    }
}

package com.super_;
public class run {
    public static void main(String[] args) {
        son son = new son();
        son.s1();
        son.s2();
    }
}

1、super细节

1> 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super;如果没有重名,使用super、this、直接访问是一样的效果

2> super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;

  如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则(根据查找的顺序),当然也需要遵守访问权限的相关规则

package com.super_;
public class father {
    public int age = 80;
    protected int height = 175;
    int salary = 8000;
    private int weight = 80;
    public void public1(){
        System.out.println("father类的public void public1()被调用");
    }
    public void public2(){
        System.out.println("father类的public void public2()被调用");
    }
}

package com.super_;
public class son extends father{
    int age = 20;
    int height = 175;
    int salary = 8000;
    int weight = 80;
    public void public1(){
        System.out.println("son类的public void public1()被调用");
    }
    public void info(){
        public1();
        //找public1()方法时(public1()和 this.public1())顺序是:
        //1、先找本类,如果有,则调用
        //2、如果没有,则找父类(父类如果有,并可以调用,则调用)
        //3、如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类
        //提示:如果查找方法的过程中,找到了,但是不能访问(private),则报错cannot access
        //如果查找方法最终都没有找到,则提示方法不存在
        super.public2();
        //找public2()方法时(super.public2();)顺序是:
        //1、直接跳过本类,去找父类
        //2、父类如果有,并可以调用,则调用
        //3、如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类
        //提示:如果查找方法的过程中,找到了,但是不能访问(private),则报错cannot access
        //如果查找方法最终都没有找到,则提示方法不存在
        System.out.println(age);  //查找属性的原理与方法一样
        System.out.println(this.age);
        System.out.println(super.age);
        //运行结果:
        //son类的public void public1()被调用
        //father类的public void public1()被调用
        //20
        //20
        //80
    }
}

package com.super_;
public class run {
    public static void main(String[] args) {
        son son = new son();
        son.info();
    }
}

2、super和this的比较

二十四、方法重写/覆盖 override

方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、形参列表一样,那么就说子类的这个方法覆盖了父类的方法

package com.super_;
public class father {
    public void m1(){  //方法名称、返回类型、型参列表都一样
        System.out.println("father类的public void m1()被调用");
    }
}


package com.super_;
public class son extends father{
    public void m1(){   //方法名称、返回类型、型参列表都一样
        System.out.println("son类的public void m1()被调用");
    }
}

1、方法重写的细节

方法重写也叫方法覆盖,需要满足下面的条件

1>子类的方法的形参列表、方法名称,要和父类方法的形参列表方法名称完全一样

2> 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类(比如父类返回类型是 Object ,子类方法返向类型是String,String是Object的子类)

package com.super_;
public class father {
    public AAA m1(){ 
        return null;
    }
}
class AAA{   //定义AAA类

}
class BBB extends AAA{  //BBB是AAA的子类

}

package com.super_;
public class son extends father{
    public BBB m1(){   //方法名称、返回类型BBB是AAA的子类、型参列表都一样
        return null;
    }
}

3> 子类方法不能缩小父类方法的访问权限 public > protected >默认>private

2、方法的重载和重写比较

二十五、多态

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的

1、多态详解

方法的多态:方法的重写和重载就体现多态

对象的多态

1> 一个对象的编译类型和运行类型可以不一致

2> 编译类型在定义对象时,就确定了,不能改变

3> 运行类型是可以变化的

4> 编译类型看定义时=号的左边,运行类型看=号的右边

5> 多态的前提是两个对象(类)存在继承关系

Animal animal = new Dog(); //animal编译类型是Animal,运行类型Dog
animal = new Cat(); //animal的运行类型变成了Cat,编译类型仍然是 Animal
package com.ploy;
public class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}


package com.ploy;
public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

package com.ploy;
public class Food {
    private String name;
    public Food(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

package com.ploy;
public class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}


package com.ploy;
public class Master {
    private String name;
    public Master(String name) {
        this.name = name;
    }
    public void feed(Animal animal , Food food){  //体现多态
    //相当于 Animal animal = new Dog("阿黄");
    //相当于 Food food= new Fish("小黄鱼");
        System.out.println("主人 " + name + " 喂 " + animal.getName() + " 吃 " + food.getName());
    }
}

package com.ploy;
public class run {
    public static void main(String[] args) {
        Master master = new Master("King");
        Dog dog = new Dog("阿黄");
        Fish fish = new Fish("小黄鱼");
        master.feed(dog,fish);
    }
}

2、向上转型

1> 本质:父类的引用指向了子类的对象

2> 语法:父类类型 引用名 = new 子类类型();

3> 特点:编译类型看左边,运行类型看右边

可以调用父类中的所有成员(需遵守访问权限)、不能调用子类中特有成员、最终运行效果看子类的查找顺序

package com.up;
public class father {
    public void eat(){
        System.out.println("father的eat方法");
    }
    public void run(){
        System.out.println("father的run方法");
    }
    private void cry(){
        System.out.println("father的cry方法");
    }
}

package com.up;
public class son extends father {
    public void eat(){  //重写了father的eat方法
        System.out.println("son的eat方法");
    }
    public void drink(){  //son的特有方法
        System.out.println("son的drink方法");
    }
}

package com.up;
public class run {
    public static void main(String[] args) {
        father temp = new son();  //父类的引用指向了子类的对象,叫向上转型
        temp.eat();  //输出结果:son的eat方法
        temp.run();  //输出结果:father的run方法
        //temp.drink();  //这样是会报错的
        //temp.cry();    //这样是会报错的
    }
}

向上转型细节

1> temp这个变量引用可以调用father类的所有属性和方法,但是要遵守权限,father类的private的方法就不能调用

2> temp不能调用son类(子类)的特有属性和方法,因为变量引用能调用哪些成员是在编译阶段,由编译类型来决定的,即与javac编译相关

3> temp调用运行方法时最终的运行效果要看son类(运行类型)的查找规则,由java这个程序运行,即调用方法时按照从子类向父类的顺序进行查找

总结一句话:调用方法看编译类型,执行顺序看运行类型

3、向下转型

1> 语法:子类类型 引用名 = (子类类型) 父类引用;

2> 只能强转父类的引用,不能强转父类的对象

3> 要求父类的引用必须指向的是当前目标类型的对象

public class run {
    public static void main(String[] args) {
        Animal temp = cat();
        Dog dog = (Dog) temp;  
        //会报错,因为原来的temp是一只羊,你不能把temp强转成一只狗
    }
}

4> 当向下转型后,可以调用子类类型中所有的成员

经过向下转型之后的son指向的也是son()这个对象,此时son的编译类型是son,所以可以调用子类类型的所有成员,因为son类还继承了father类,所以son也可以调用father类的成员

4、多态注意事项

1> 属性没有重写之说,属性的值看编译的类型

package com.up;
public class run {
    public static void main(String[] args) {
        father temp = new son();
        System.out.println(temp.age);  //输出结果:50
        System.out.println(((son) temp).age); //输出结果:25
    }
}
class father{
    int age = 50;
}
class son extends father{
    int age = 25;
}

2> instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

package com.up;
public class run {
    public static void main(String[] args) {
        String name = "King";
        father temp = new son();
        System.out.println(temp instanceof son);  //输出结果:true
        System.out.println(temp instanceof father); //输出结果:true
        System.out.println(name instanceof Object); //输出结果:true
    }
}
class father{
    int age = 50;
}
class son extends father{
    int age = 25;
}

3> 向上转型的特殊写法

package com.up;
public class run {
    public static void main(String[] args) {
        son s_temp = new son();
        father f_temp = s_temp;  //向上转型,相当于father f_temp = new son();
//注意:s_temp和f_temp是指向同一个对象的
} } class father{ int age = 50; } class son extends father{ int age = 25; }

5、练习题

package com.up;
public class run {
    public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.count);  //20  属性的值看编译的类型
        s.display();                  //20
        Base b = s;
        System.out.println(b == s);   //两个对象比较的是地址  true
        System.out.println(b.count);  //10  属性的值看编译的类型
        b.display();                  //20  向上转型调用运行方法时最终的运行效果要看运行类型的查找规则,由java这个程序运行,即调用方法时按照从子类向父类的顺序进行查找
    }
}
class Base{
    int count = 10;
    public void display(){
        System.out.println(this.count);
    }
}
class Sub extends Base{
    int count= 20;
    public void display(){
        System.out.println(this.count);
    }
}

6、动态绑定机制

1> 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

2> 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

二十六、多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

package com.ployarr;
public class run {
    public static void main(String[] args) {
        Object[] array = {"King",20,52.10,'A'};
        for(int i = 0 ; i < array.length ; i++){
            System.out.println(array[i]);
        }
    }
}

应用实例

现有一个继承结构如下:

要求创建1个Person对象、2个Student对象、2个Teacher对象,统一放在数组中,并调用每个对象的say方法,并且想办法调用Student对象和Teacher对象的私有方法

package com.ployarr;
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String say(){
        return name + "\t" + age;
    }
}


package com.ployarr;
public class Teacher extends Person{
    private double salary;
    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }
    public String say(){  //重写了父类的say方法
        return "老师\t" + super.say() + "\t" + salary;
    }
    public void teach(){  //Teacher类的特有方法
        System.out.println("老师" + getName() + "正在教学中……");
    }
}


package com.ployarr;
public class Student extends Person{
    private double source;
    public Student(String name, int age, double source) {
        super(name, age);
        this.source = source;
    }
    public Double getSource() {
        return source;
    }
    public void setSource(Double source) {
        this.source = source;
    }
    public String say(){  //重写了父类的say方法
        return "学生\t" + super.say() + "\t" + source;
    }
    public void study(){  //Student类的特有方法
        System.out.println("学生" + getName() + "正在学习中……");
    }
}


package com.ployarr;
public class run {
    public static void main(String[] args) {
        Person[] array = new Person[5];
        array[0] = new Person("zrh",20);
        array[1] = new Student("rxk",21,100);
        array[2] = new Student("ym",30,95);
        array[3] = new Teacher("wxn",30,8000);
        array[4] = new Teacher("xxn",30,7000);
        for(int i = 0 ; i < array.length ; i++){
            System.out.println(array[i].say());
            //array[i].study或者 array[i].teach是错误的
            // 因为他们的编译类型中没有此方法,能调用哪些方法是看编译类型的
            //所以可以使用向下转型+判断
            if(array[i] instanceof Student){
                ((Student)array[i]).study();
            } else if(array[i] instanceof Teacher){
                ((Teacher)array[i]).teach();
            } else if(array[i] instanceof Person);
            else {
                System.out.println("输入的类型错误");
            }
        }
    }
}

二十七、多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型,应用实例:前面的主人喂动物

应用实例:

定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法

普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

package com.ploywork;
public class Employee {
    private String name;
    private double salary;
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public double getAnnual(){
        return salary * 12;
    }
}


package com.ploywork;
public class ordinary extends Employee{
    public ordinary(String name, double salary) {
        super(name, salary);
    }
    public void work(){
        System.out.println("普通员工" + getName() + "正在工作");
    }
    public double getAnnual(){  //重写了父类的getAnnual方法
        return getSalary() * 12;
    }
}


package com.ploywork;
public class manager extends Employee{
    private double bonus;
    public manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }
    public double getBonus() {
        return bonus;
    }
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
    public void manage(){
        System.out.println(getName() + "经理的manage方法");
    }
    public double getAnnual(){ //重写了父类的getAnnual方法
        return getSalary() * 12 + bonus;
    }
}


package com.ploywork;
public class run {
    public static void main(String[] args) {
        run run = new run();
        Employee temp = new ordinary("rxk",5000);
        System.out.println(run.showEmpAunnal(temp));
        run.testWork(temp);
        Employee temp1 = new manager("rxk",8000,2000);
        System.out.println(run.showEmpAunnal(temp1));
        run.testWork(temp1);
    }
    public double showEmpAunnal(Employee e){
        return e.getAnnual();
    }
    public void testWork(Employee e){
        if(e instanceof ordinary){
            ((ordinary) e).work();  //向下转型
        } else if (e instanceof manager){
            ((manager) e).manage();  //向下转型
        }
    }
}

二十八、== 和 equals 的对比

==是一个比较运算符

1、 == 既可以判断基本类型,又可以判断引用类型

2、 == 如果判断基本类型,判断的是值是否相等(示例:int i=10; double d=10.0;)

3.、== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

package com.com;
public class equals_ {
    public static void main(String[] args) {
        Integer num1 = new Integer(5);
        Integer num2 = new Integer(5);
        //== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
        System.out.println(num1 == num2);   //false
        int num3 = 50;
        double num4 = 50.0;
        //== 如果判断基本类型,判断的是值是否相等
        System.out.println(num3 == num4);  //true
    }
}

4、equals 是Object类中的方法,只能判断引用类型

5、equals默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等

//Object类的equals方法
public boolean equals(Object obj) {
        return (this == obj);
    }
//String类重写了equals方法
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String aString = (String)anObject;
            if (coder() == aString.coder()) {
                return isLatin1() ? StringLatin1.equals(value, aString.value)
                                  : StringUTF16.equals(value, aString.value);
            }
        }
        return false;
    }
//Integer类重写了equals方法
public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
package com.com;
public class equals_ {
    public static void main(String[] args) {
        Object temp = new Object();
        Object test = temp;
        //equals默认判断的是地址是否相等
        System.out.println(temp.equals(test));  //true
        String str = "King";
        String str1 = "King";
        String str2 = "mark";
        //子类中往往重写该方法,用于判断内容是否相等
        System.out.println(str.equals(str1));  //true
        System.out.println(str.equals(str2));  //false
    }
}

备注:ctrl + B 可以查看当前的方法的JDK源码

应用实例:判断两个tools对象的内容是否相等,如果两个tools对象的各个属性值都一样,则返回true,反之false

package com.equalshomework;
public class Person{
    public static void main(String[] args) {
        tools a = new tools("zrh",20,'男');
        tools b = new tools("zrh",20,'男');
        System.out.println(a.equals(b));
    }
}
class tools {
    private String name;
    private int age;
    private char gender;
    public tools(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    public boolean equals(Object obj){  //重写了equals方法
        if(this == obj){  //== 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
            return true;
        }
        else if(obj instanceof tools){
            tools t = (tools)obj;  //向下转型,为了可以拿到对象的属性值
            return this.name.equals(t.name) && this.age == t.age && this.gender == t.gender;
        } return false;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public char getGender() {
        return gender;
    }
    public void setGender(char gender) {
        this.gender = gender;
    }
}

二十九、hashCode方法

1、提高具有哈希结构的容器的效率

2、两个引用,如果指向的是同一个对象,则哈希值肯定是一样的

3、两个引用,如果指向的是不同对象,则哈希值是不一样的

4、哈希值主要根据地址号来的,不能完全将哈希值等价于地址

5、在集合中hashCode如果有需要的话,会进行重写

package com.hashCode;
public class hashCode {
    public static void main(String[] args) {
        test test1 = new test();
        test test2 = new test();
        test test3 = test1;
        System.out.println(test1.hashCode());  //1239731077
        System.out.println(test2.hashCode());  //557041912
        System.out.println(test3.hashCode());  //1239731077
    }
}
class test{

}

三十、toString方法

基本介绍

默认返回:全类名+@+哈希值的十六进制

1、子类往往重写toString方法,用于返回对象的属性信息

2、重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式.

3、当直接输出一个对象时,toString方法会被默认的调用,比如System.out.printIn(monster);就会默认调用monster.toString()

//查看Object 的toString 方法
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
package com.toString_;
public class toString_{
    public static void main(String[]args){
        cat cat = new cat();
        System.out.println(cat.toString());  //com.toString_.cat@49e4cb85
        master master = new master();
        System.out.println(master.toString()); //master{name='King', age=20}
        // 当直接输出一个对象时,toString方法会被默认的调用
        System.out.println(cat);  //等价于System.out.println(cat.toString());
        System.out.println(master);  //等价于System.out.println(master.toString());
    }
}
class cat{

}
class master{
    private String name = "King";
    private int age = 20;
    @Override
    public String toString() {
        return "master{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

三十一、finalize方法

1、当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作

2、什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法

3、垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制

我们在实际开发中,几乎不会运用finalize,所以更多就是为了应付面试

package com.finalize_;
public class finalize_ {
    public static void main(String[] args) {
        car car = new car("宝马");
        car = null;
        //这时 car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象
        // 在销毁对象前,会调用该对象的finalize方法
        // 程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
        //如果程序员不重写 finalize,那么就会调用Object类的 finalize,即默认处理
        System.gc();
        System.out.println("程序执行完毕");
    }
}
class car{
    String name;

    public car(String name) {
        this.name = name;
    }
    @Override
    protected void finalize() throws Throwable {
        System.out.println("释放资源……");
    }
}
//执行结果:
//释放资源……
//程序执行完毕

三十二、断点调试

F7(跳入方法内)

alt+shift+F7(强制进入)

F8(逐行执行代码)

shift+F8(跳出方法)

F9(resume,直接执行到下一个断点)

Idea debug如何进入Jdk源码

解决方法1:使用force step into : 快捷键 alt + shift + F7 

解决方法2:配置一下就好了

点击Setting --> Build,Execution,Deployment --> Debugger --> Stepping

把Do not step into the classes中的java.*,javax.*取消勾选

三十三、零钱通案例

//面向过程版本
package com.tencent.smallchange;
import java.text.SimpleDateFormat;
import java.util.Scanner;
import java.util.Date;
public class run {
    public static void main(String[] args) {
        boolean loop = true;
        Date date;  //用于新建一个Date对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String details = "";
        double money = 0.0;  //接收收益入账金额
        double cash = 0.0;   //用户余额
        Scanner scanner = new Scanner(System.in);
        do {
            System.out.println("--------零钱通---------");
            System.out.println("\t1、零钱通明细");
            System.out.println("\t2、收益入账");
            System.out.println("\t3、消费支出");
            System.out.println("\t4、退出程序");
            System.out.print("请选择功能(1--4):");
            String num = scanner.next();
            date = new Date();  //新建一个Date对象
            String time = sdf.format(date);  //将Date的时间格式改为第9行所示
            switch (num){
                case "1":  // 零钱通明细
                    System.out.print("-------零钱通明细-------");
                    System.out.println(details);
                    break;
                case "2":  //收益入账
                    System.out.println("--------收益入账--------");
                    //判断收益金额的有效性
                    while(true){
                        System.out.print("请输入收益金额:(要求:金额>0)");
                        money = scanner.nextDouble();  //接收收益的金额
                        if(money <= 0){  //如果收益的金额为负
                            System.out.println("金额输入错误,请核对");
                        } else break;
                    }
                    cash += money;  //累加收益金额
                    details += "\n收益入账\t金额:+" + money + "\t时间:" + time + "\t余额:" +cash;
                    break;
                case "3":  //消费支出
                    System.out.println("--------消费支出--------");
                    //这里不加while的原因是因为用户可能在发现余额不足的情况下会不再进行购买,转而进行查看明细
                    System.out.print("请输入该项目消费金额:");
                    double out_money = scanner.nextDouble();  //接收消费的金额
                    if(out_money > cash){
                        System.out.println("您的余额不足,支付失败,请及时充值");
                        break;
                    }
                    System.out.print("请输入消费支出项目:");
                    String item = scanner.next();
                    cash -= out_money;  //累减消费金额
                    details += "\n" + item + "\t金额:-" + out_money + "\t时间:" + time + "\t余额:" + cash;
                    break;
                case "4":  //退出程序
                    String log;
                    while(true){
                        System.out.println("你确定要退出此程序?(y/n):");
                        log = scanner.next();
                        if(log.equals("y") || log.equals("n")){  //如果输入不是y/n,则一直循环下去
                            break;
                        }
                    }
                    if("y".equals(log)){
                        loop = false;  //终止程序循环
                        break;
                    }
                    break;
                default:
                    System.out.println("你输入的菜单有误,请重新输入......");
            }
        }while(loop);
        System.out.println("程序已退出......");
    }
}
//面向对象版本
package com.tencent.smallchangeOOP;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class changeMoney {
    //定义属性
    boolean loop = true;  //用于终结run()方法中的do while循环
    Date date;  //用于接收下面新建的Date对象
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String details = "";  //记录零钱明细
    double money = 0.0;  //接收收益入账金额
    double cash = 0.0;   //用户余额
    Scanner scanner = new Scanner(System.in);  //创建scanner对象用于接收用户输入的内容
    //定义方法
    public void run(){  //运行程序,程序中调用了许多方法
        do {
            System.out.println("--------零钱通---------");
            System.out.println("\t1、零钱通明细");
            System.out.println("\t2、收益入账");
            System.out.println("\t3、消费支出");
            System.out.println("\t4、退出程序");
            System.out.print("请选择功能(1--4):");
            String num = scanner.next();
            switch (num){
                case "1":
                    details();
                    break;
                case "2":
                    profit();
                    break;
                case "3":
                    consumption();
                    break;
                case "4":
                    quit();
                    break;
                default:
                    System.out.print("你是输入的指令有误,请重新输入:");
            }
        } while(loop);
        System.out.println("程序已退出......");
    }
    public void details(){  // 零钱通明细
        System.out.print("-------零钱通明细-------");
        System.out.println(details);
    }
    public void profit(){  //收益入账
        date = new Date();
        String time = sdf.format(date);
        System.out.println("--------收益入账--------");
        //判断收益金额的有效性
        while(true){
            System.out.print("请输入收益金额:(要求:金额>0)");
            money = scanner.nextDouble();  //接收收益的金额
            if(money <= 0){  //如果收益的金额为负
                System.out.println("金额输入错误,请核对");
            } else break;
        }
        cash += money;  //累加收益金额
        details += "\n收益入账\t金额:+" + money + "\t时间:" + time + "\t余额:" +cash;
    }
    public void consumption(){  //消费支出
        date = new Date();
        String time = sdf.format(date);
        System.out.println("--------消费支出--------");
        //这里不加while的原因是因为用户可能在发现余额不足的情况下会不再进行购买,转而进行查看明细的操作
        System.out.print("请输入该项目消费金额:");
        double out_money = scanner.nextDouble();  //接收消费的金额
        if(out_money > cash){
            System.out.println("您的余额不足,支付失败,请及时充值");
            return;
        }
        System.out.print("请输入消费支出项目:");
        String item = scanner.next();
        cash -= out_money;  //累减消费金额
        details += "\n" + item + "\t金额:-" + out_money + "\t时间:" + time + "\t余额:" + cash;
    }
    public void quit(){  //退出程序
        String log;
        while(true){
            System.out.println("你确定要退出此程序?(y/n):");
            log = scanner.next();
            if(log.equals("y") || log.equals("n")){  //如果输入不是y/n,则一直循环下去
                break;
            }
        }
        if("y".equals(log)){
            loop = false;  //终止程序循环
            return;
        }

    }
}

package com.tencent.smallchangeOOP;
public class test_run {
    public static void main(String[] args) {
        changeMoney changeMoney = new changeMoney();
        changeMoney.run();
    }
}

三十四、房屋出租系统

工具集类为将事先用到的方法都先写好,在service类里直接调用即可,例如:接受用户输入+输入正确值判断等

我们在拿到需求进行分析的时候,应该从用户的角度去进行分析,在代码实现的时候应该从下往上去实现

package test;
public class test {
    public static void main(String[] args) {
        run.A();  //因为A方法为static,所以不需要创建对象就可以直接调用
        //运行结果:test包下的static的A方法
    }
}

package test;
public class run {
public static void A(){
    System.out.println("test包下的static的A方法");
}
}

三十五、练习题

//定义一个Person类(name,age,job),初始化Person对象数组,有4个person对象,按照age从大到小进行排序,使用冒泡排序
package com.homework;
public class problem01 {
    public static void main(String[] args) {
        Person[] object = new Person[4];  //这里的编译类型可以写父类,也可以写本类,千万不要被多态搞晕
        object[0] = new Person("Tom", 25, "警察");
        object[1] = new Person("Jerry", 20, "医生");
        object[2] = new Person("hash", 21, "舞者");
        object[3] = new Person("Mary", 18, "收银");
        //冒泡排序代码
        Person temp = null;
        for (int i = 0; i < object.length - 1; i++) {
            for (int j = 0 ; j < object.length - 1 - i ; j++){
                if (object[j].getAge() > object[j + 1].getAge()) {
                    temp = object[j];
                    object[j] = object[j + 1];
                    object[j + 1] = temp;
                }
            }
        }
        for (int i = 0; i < object.length; i++) {
            System.out.println(object[i]);  //默认使用object[i].toString()

        }
    }
}

class Person{
    private String name;
    private int age;
    private String job;
    public Person(String name, int age, String job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", job='" + job + '\'' +
                '}';
    }
}
//在BankAccount类的基础上扩展新类CheckingAccount,对每次存款和取款都收取1美元的手续费
package com.homework5;
public class BankAccount {
    private int balance;  //定义私有属性
    //构造器
    public BankAccount(int balance) {
        this.balance = balance;
    }
    //存款
    public void deposit(double amount){
        balance += amount;
    }
    //取款
    public void withdraw(double amount){
        balance -= amount;
    }
    //get、set
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
}

package com.homework5;
public class CheckingAccount extends BankAccount{
    public CheckingAccount(int balance) {
        super(balance);
    }
    public void deposit(double amount){
        super.deposit(amount - 1); //巧妙的传入了父类的方法参数
    }
    public void withdraw(double amount){
        super.withdraw(amount + 1); //巧妙的传入了父类的方法参数
    }
}


package com.homework5;
public class run {
    public static void main(String[] args) {
        CheckingAccount checkingAccount = new CheckingAccount(5000);
        checkingAccount.deposit(100);
        System.out.println(checkingAccount.getBalance());  //5099
        checkingAccount.withdraw(100);
        System.out.println(checkingAccount.getBalance());  //4998
    }
}

 

posted @ 2021-11-04 17:12  CentKiller  阅读(145)  评论(6编辑  收藏  举报