Java基础

一、使用记事本编写运行Java程序

1.1、编写Java源程序(任何一个文本编辑器来编写)

a.Java源程序可以用一个空白记事本,然后如是输入以下内容

/*

*第一个java源程序

*/

public class HelloJava{

//这里是程序入口

public static void main(String args[]){

//输出字符串

Stytem.out.ptintln("Hello Java");

}}

b.对原代码中的重要组成元素进行简单介绍

关键字:

public表示说明符,表明类的一个公共类,可以控制其他对象对类成员的访问

class用于表明一个类,其后所跟字符串是类的名称

static表示该方法的一个静态方法,允许调用main函数,无法创建类的实例

void表示main()方法没有返回值

c."/""/"之间的内容和以"//"开始的内容为java的程序的注释

1.2、将写完代码后的文件另存为HelloJava.java,保存类型选择所有为文件

编译运行使用JDK中的javac的命令:

打开cmd,输入javac HelloWorld.java继续编译

运行编译.class文件,输入指令java Hello World

1.3、Java程序运行机制

编译型

解释型

程序运行机制

Java源文件(*. java) ——>Java编译器——>字节码文件(*. class)——>类装载器

字节码校检器──>解释器 ->操作系统(Windows、Linux等

二、Java语法基础

2.1、Java标识符和关键字

标识符

Java中标识符是为方法、变量或其他用户定义项所定义的名称。标识符可以有一个或多个字符。

在Java语言中,标识符的构成规则如下。

标识符由数字(0~9)和字母(A~Z 和 a~z)、美元符号($)、下划线(_)以及 Unicode 字

符集中符号大于 0xC0 的所有符号组合构成(各符号之间没有空格)

注意:数字不能作为标识符的开始:如1ac

关键字:具有特殊含义的标识符例如:int、void、main.......

用户自定义标识符:是由用户标识符生成的非保留字的标识符如:abc、name

2.2、命名规则

Java类的命名规则:

类名首字母大写,如果有多个单词组合,则每个单词首字母大写(大驼峰命名法)HelloWorld

小驼峰命名法:首个单词字母小型,后面的单词首字母大写

如:helloworld,Demo01

注意:java严格区分大小,如:name和Name表达意思不一样

2.3、javadoc文档注释

4.java变量的声明和初始化

java语言石墙类型(strongly Typed)语言,强类型

课堂实例:

*变量命名:

   *语法:变量类型 变量名字 赋值符号 值:

         */

   int age:// 声明变量,不赋值

   age=32;// 声明变量之后赋值

   String name="大锤"  //声明变量,赋值

         //修改变量值(重新赋值)

         age =56;

         name ="张三";

         //  int name;

         //  num="北京中关村“

         //  int 2num://非法命名

         int n1,n2,n3,n4;//批量命名

         n1 = 2;

         n2 = 3;

         n3 = 4;

         n1= n2 = n3 = n4 = 56;

         int num1 = 3,num2 =4;

/**

     * 变量的作用域:

     *   全局变量:

     *    定义在方法(函数)和代码块之外的变量

     *      局部变量

     * 定义在方法或者代码块之间的变量

     */

    //定义全局变量

int age ;//全局变量

String name ="大锤"

        //静态变量

static int number;

double price = 1399.99;

public static void main(String arsg[]){

//定义变量

    int age;

    if(true)

Java核心编程实验课笔记

一、Java常量

常量是指在程序的整个运行过程中值保持不变的量。

注意常量和常量值是不同的概念,常量值是常量的具体和直观的表现形式,

常量是形式化的表现。通常在程序中既可以直接使用常量值,也可以使用常量。

1、常量值

常量值又称为字面常量,它是通过数据直接表示的,因此有很多种数据类型,像整型和字符串型等。下面一一介绍这些常量值。

2、整型常量值

Java 的整型常量值主要有如下 3 种形式:

十进制数形式:**如 54、-67、0。

八进制数形式:**Java 中的八进制常数的表示以 0 开头,如 0125 表示十进制数: 85,-013 表示十进制数 -11。

十六进制数形式:Java中的十六进制常数的表示以0x或0X开头,如0x100表示十进制数256, -0x16 表示十进制数-22。

整型(int)常量默认在内存中占 32 位

长整型数,它在内存中占 64 位。

3、实型常量值

Java的实型常量值主要有如下两种形式。

十进制数形式:由数字和小数点组成,且必须有小数点,如12.34、-98.0。

科学记数法形式:如1.75e5或32&E3,中e或E之前必须有数字,且e或E之后的数字必须为整数。

Java实型常量默认在内存中占64位,是具有双精度型(double) 的值。

单精度型数值一般要在该常数后面加 F或f,如69.7f, 表示-个float型实数,它在内存中占32位。

4、布尔型常量值

Java的布尔型常量只有两个值,即false (假)和true (真)。

二、Java变量的作用域

变量的作用域规定了变量所能使用的范围,只有在作用域范围内才能被使用。根据变量的声明地点不同,变量的作用域也不同

变量又分为成员变量与局部变量

成员变量分为

  1. 全局变量:无static修饰访问对象名变量名
  2. 静态变量:用static修饰,访问类名变量名

局部变量分为

  1. 方法参数变量:在整个方法内有下效
  2. 方法局部变量:从定义这个变量开始到方法结束这一段时间内有效
  3. 代码块局部变量:从定义这个变量开始到代码结束这一段时间内有效

局部变量在使用前必须被程序员主动初始化值

声明变量

首字符必须是字母、下划线、美元符号、人民币符号

标识符由数字大写字母小写字母下划线美元符号人民币符号以及所有在十六进制Oxco前的ASCLL码组成,不能吧关键字保留字作为标识符

三、Java中的数据类型:

基本数据类型包括 :boolean(布尔型)、float(单精度浮点型)、char(字符型)、byte(字节型)、short(短整型)int(整型)、long(长整型)和 double (双精度浮点型)共 8 种

1.字节型:byte by = 127;

 // System.out.println((byte) (by + 1));

 2.短整型:short sh = 56;

 3.整形: int num = 78946;

 4.长整型

注意: 需要早长整型末尾添加L或者l:long number = 56L;

 5.单精度浮点型

注意: 需要早长整型末尾添加F或者f;float fl = 3.14F

 System.out.println(fl1);

 6.双精度浮点型,java中小数默认是double类型;double db = 3.14159;

 7.字符型

字符型的值使用单引号引起来,当值为整形时不需要单引号

        char ch1 = 3;

        char ch2 = 'a';

        char ch3 = 97;

        System.out.println(ch1);

        System.out.println(ch3);

 8.布尔值类型

注意:Java中布尔值类型只有true和false两个值

        boolean bool1 = true;

        boolean bool2 = false;

2、引用数据类型

除了基本数据类型,其他全部是引用数类型,如:String、数组等

一、数据类型转换

数据类型的转换是在所赋值的数值类型和被变量接收的数据类型不一致时发生的,它需要从一种数据类型转换成另一种数据类型。数据类型的转换可以分为隐式转换(自动类型转换)和显式转换(强制类型转换)两种。

  1. 隐式转换(自动类型转换):小类型数据转换为大类型数据

满足条件:1、两种数据类型彼此兼容             

2、目标类型的取值范围大于源数据类型(低级类型数据 转换成高级类型数据

3、在运算过程中,由于不同的数据类型会转换成同一种数据类型, 所以整型、浮点型以及字符型都可以参与混合运算。自动转换的规则是从低级类型数据转换成高级类型数据。转换规则如下:       

1、数值型数据的转换:byteshortintlongfloatdouble              2、字符型转换为整型:charint

    以上数据类型的转换遵循从左到右的转换顺序,最终转换成表达式中 表示范围最大的变量的数据类型。

2、显示转换(强制类型转换)大类型数据转换为小类型数据需要强制转换

语法:小类型变量名 = (需要转换的类型 (小类型)) 大类型;
注意:大类型数据转换为小类型数据有可能会造成精度丢失和溢出
short类型和char类型在转换时都需要强制转换

String str1="Hello";
int
 num1=(int)str1;//不能强转;
String
 str1=(String)num1;//不能强转;
注意:两种数据类型彼此兼容,否则不能互相转换
基本数据类型不能与引用数据类型转换

注意:char 类型比较特殊,char 自动转换成 int、long、float 和 double,但 byte 和 short 不能自动转换为 char,而且 char 也不能自动转换为 byte 或 short。

注意:+号可以用作算术符号,也可以做字符串拼接//这样就可以相加。
任何数据类型与字符串做+运算都会变为字符串


b1+num将数据类型统一之后在运算(往大类型数据统一)
Java运算符:
最基本的运算符包括算术运算符、赋值运算符、逻辑运算符和关系运算符等

代码实例

package com.zpark.day03;

public class Demo03 {

public static void main(String[] args) {

        byte b1 = 12;

        int num = 13;

   byte b2;

// 将b1+num的结果赋值给b2

 // b1 + num 将数据类型统一之后再运算(往大类型数据统一)

 b2 = (byte) (b1 + num);

 double d1 = 3.14159;

char ch1;

一、一元运算符

算术一元运算一共有 3 个,分别是 -、++ 和 –

a 是对 a 取反运算,a++  a-- 是在表达式运算完后,再给 a 加一或减一。而 ++a  --a 是先给 a 加一或减一,然后再进行表达式运算。

二、二元运算符

+        |     |  a  b 的和,还可用于 String 类型,进行字符串连接操作 | a + b || -        |     |  a  b 的差                                           | a - b || *        |     |  a 乘以 b 的积                                         | a * b || /        |     |  a 除以 b 的商                                         | a / b || %        | 取余  |  a 除以 b 的余数                                       | a % b |

二、Java 语言中算术运算符的功能是进行算术运算,

         * 除了经常使用的加(+)、减(-)、乘(`*`)和除(\)外,

         * 还有取模运算(%)。加(+)、减(-)、乘(`*`)、除(\)

         * 和我们平常接触的数学运算具有相同的含义

三、 java中赋值运算符:

         *          =:赋值符号

         *          +=:如, num += 2;  -----> 本质:num = num + 2;

         *          -=: 如, num -= 2;  -----> 本质:num = num - 2;

         *          *=: 如, num *= 2;  -----> 本质:num = num * 2;

         *          /=: 如, num /= 2;  -----> 本质:num = num / 2;

         *          %=: 如, num %= 2;  -----> 本质:num = num % 2;    

四、java中逻辑运算符:

         *      与、或、非

         *      &&(短路与):如:a && b, 如果ab全部为true则结果为true,否则为false

         *      ||(短路或):如:a || b, a或者b有一个为true,或者ab均为true,则为true,否则为false

         *      !:a为true,则结果为false,a为false,则结果为true

         */

  • 注意:&& 与 & 区别:如果 a 为 false,则不计算 b(因为不论 b 为何值,结果都为 false)
  • || 与 | 区别:如果 a 为 true,则不计算 b(因为不论 b 为何值,结果都为 true)

    注意:短路与(&&)和短路或(||)能够采用最优化的计算方式,从而提高效率。在实际编程时,应该优先考虑使用短路与和短路或。

        结果为 boolean 型的变量或表达式可以通过逻辑运算符结合成为逻辑表达式。逻辑运算符 &&、|| 和 !按表 2 进行逻辑运算。

实例

package com.zpark.day05;

import java.util.Scanner;

public class Demo04 {

    public static void main(String[] args) {

        /**

         * 1、定义String类型变量 name

         * 2、定义int类型变量 age

         * 3、定义double类型变量 height

         * 4、定义boolean类型变量 flag

         * 5、创建Scanner扫描器对象

         * 6、提示用户输入用户的名字、年龄、身高、flag

         * 7、获取用户输入的名字、年龄、身高、flag,并且给相应的变量赋值

         * 8、将获取的数据打印到控制台

         *      如:name = “张三”, age = 25, height = 199.5, flag = true

         */

        String name;

        int age;

        double height;

        boolean flag;

        // 创建扫描器对象

        Scanner scan = new Scanner(System.in);

        System.out.println("请输入您的名字:");

        name = scan.next();

        System.out.println("请输入您的年龄:");

        age = scan.nextInt();

        System.out.println("请输入您的身高:");

        height = scan.nextDouble();

        System.out.println("请输入状态:");

        flag = scan.nextBoolean();

        System.out.println("name = " + name + ", age = " + age +

                ", height = " + height + ", flag = " + flag);

    }

}

一、.关系运算符

关系运算符(relational operators)也可以称为“比较运算符”,用于用来比较判断两个变量或常量的大小。关系运算符是二元运算符,运算结果是 boolean 型。当运算符对应的关系成立时,运算结果是 true,否则是 false。

注意点如下所示:

 1、基本类型的变量、值不能和引用类型的变量、值使用 == 进行比较;boolean 类型的变量、值不能与其他任意类型的变量、值使用 == 进行比较;如果两个引用类型之间没有父子继承关系,那么它们的变量也不能使用 == 进行比较。       

2、== 和 != 可以应用于基本数据类型和引用类型。当用于引用类型比较时,比较的是两个引用是否指向同一个对象,但当时实际开发过程多数情况下,只是比较对象的内容是否相当,不需要比较是否为同一个对象。

实例

public class Demo02 {

    public static void main(String[] args) {

        /**

         * 当引用数据类型使用==和!=做比较运算时的区别:

         *

         */

        String s1 = "Hello";

        String s2 = "Hello";

        String s3 = new String("Hello");

        System.out.println(s1 == s2);

        System.out.println(s1 == s3);

        /**

         * s1.equals(s3):比较s1与s3的内容是否相同

         */

        System.out.println(s1.equals(s3));

关系运算符的优先级为:>、<、>=、<= 具有相同的优先级,并且高于具有相同优先级的 !=、==。关系运算符的优先级高于赋值运算符而低于算术运算符,结合方向是自左向右。

3、equals(obj)方法和 == 的区别:

         *      equals方法比较的是两个对象的内容是否相等

         *      == 比较的是两个对象再内存中存放的地址是否为同一个

         */二、Scanner的基本使用

public class Demo03 {

    public static void main(String[] args) {

        /**

         * java扫描器:用户从控制台输入数据,后台Java程序接收

         *

         */

        // 创建扫描器对象

        Scanner scan = new Scanner(System.in);

        //System.out.println("请输入您的年龄:");

        // 使用scan.nextInt()方法获取用户输入的年龄(整数)

        //int age = scan.nextInt();

        //System.out.println(age);

        // 获取用户输入的小数

       // System.out.println("请输入商品价格:");

        //System.out.println(scan.nextDouble());

        // 获取用户输入的字符串

        //System.out.println("请输入您的名字:");

        System.out.println("请您写一句诗:");

        // System.out.println(scan.next());

        System.out.println(scan.nextLine());

        /**

         * next()与nextLine()方法的区别:

         *      1、当遇到空格或者回车的时候 next()方法停止读取

         *      2、当遇到回车的时候 nextLine()方法停止读取,读取整行数据

         */

    }

三、位运算符

位运算符主要用来对操作数二进制的位进行运算。按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整型值。

位逻辑运算符

位逻辑运算符包含 4 个:&(与)、|(或)、~(非)和 ^(异或)。除了 ~(即位取反)为单目运算符外,其余都为双目运算符。下表中列出了它们的基本用法。

运算符

含义

实例

结果

&

按位进行与运算(AND)

4 & 5

4

|

按位进行或运算(OR)

4 | 5

5

^

按位进行异或运算(XOR)

4 ^ 5

1

~

按位进行取反运算(NOT)

~ 4

-5

2、位与运算符

        位与运算符为&,其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位同时为 1,那么计算结果才为 1,否则为 0。因此,任何数与 0 进行按位与运算,其结果都为 0。

3、位或运算符

       

其运算规则是:参与运算的数字,低位对齐,高位不足的补零。如果对应的二进制位只要有一个为 1,那么结果就为 1;如果对应的二进制位都为 0,结果才为 0。

4、位异或运算符

        位异或运算符为^,其运算规则是:参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位相同(同时为 0 或同时为 1)时,结果为 0;如果对应的二进制位不相同,结果则为 1。

位取反运算符

        位取反运算符为~,其运算规则是:只对一个操作数进行运算,将操作数二进制中的 1 改为 0,0 改为 1。

(9.5)Java 流程控制语句

1、语句编写方式:在 Java 中,语句是最小的组成单位,每个语句必须使用分号作为结束符。

2、语句表达式:

在很多的高级语言中,有专门的赋值语句。而在 Java 中将赋值作为一个运算符,因此只有赋值表达式。在赋值表达式后面添加分号就成了独立的语句。

3、复合语句:复合语句又称为语句块,是很多个语句的组合,从而可以将多个语句看作单个语句。

一、if_else语句

选择结构(也叫分支结构)解决了顺序结构不能判断的缺点,可以根据一个条件判断执行哪些语句块。选择结构适合于带有逻辑或关系比较等条件判断的计算。例如,判断是否到下班时间,判断两个数的大小等。

if 语句是使用最多的条件分支结构,它属于选择语句,也可以称为条件语句。if 选择结构是根据条件判断之后再做处理的一种语法结构。默认情况下,if 语句控制着下方紧跟的一条语句的执行。不过,通过语句块,if 语句可以控制多个语句。if 语句的最简语法格式如下,表示“如果满足某种条件,就进行某种处理”。

课堂实例:

public class Demo01 {
    public static void main(String[] args) {

        /**
         * 单分支结构:
         *     if () {
         *
         *     }
         */


        /**
         * 双分支结构
         * if语句:
         *      if (条件表达式) {
         *          // 条件为true执行
         *          // 代码块
         *      } else {
         *          // 条件为false执行
         *      }
         *
         *      案例:要求用户输入自己的年龄,然后判断其是否成年
         */
        // 

if 语句的主要功能:是给程序提供一个分支。然而,有时候程序中仅仅多一个分支是远远不够的,甚至有时候程序的分支会很复杂,这就需要使用多分支的 if…else if 语句。

通常表现为“如果满足某种条件,就进行某种处理,否则如果满足另一种条件才执行另一种处理……,这些条件都不满足则执行最后一种条件”。if…else ifif (表达式) {

    语句块1;

} else {

    语句块2;

} 多分支语句的语法格式如下所示:

if(表达式1) {

    语句块1;

} else if(表达式2) {

    语句块2;

...

} else if(表达式n) {

    语句块n;

} else {

    语句块n+1;

}

课堂实例:

/**

*单分支结构

*if(){

*}

*/

/**

*双分支结构

*if语句:

*If(条件表达式){

*//条件为true

*//代码块

*}else{

*条件语句为false执行

*}

/**

*多分支结构

*if(){

*}else if (){

*} else if(){

*}…….{

/***
         *f嵌套
          *      if(boolean){
         *          if(boolean){
         *              ....
         *          }
         *      }else{
         *          if(boolean){
         *              ....
         *          }

*//最后的else语句可要可不要

案例:QQ登录功能,登录流程:1、用户输入用户名,2、用户输入密码,3、点击登录按钮
         */

   // 第一种情况:先判断用户名,然后判断密码
        System.out.println("*******************************************");
        System.out.println("请输入您的用户名:");
        String username = scan.next();
        System.out.println("请输入您的密码:");
        String password = scan.next();
        // 判断用户名是否正确
        if ("2227894845".equals(username)) {
            // 用户名正确,判断密码是否正确
            if ("123456".equals(password)) {
                // 密码正确
                System.out.println("恭喜你登成功");
            }else{
                // 密码错误
                System.out.println("密码错误");
            }
        }else{
            // 用户名错误,结束登录
            System.out.println("用户名错误");
        }
        // 第二种情况:直接同判断用户名和密码
    }
}

三目运算符

Java 提供了一个特别的三元运算符(也叫三目运算符)。三元运算符的符号表示为“?:”,使用该运算符时需要有三个操作数,因此称其为三目运算符。使用条件运算符的一般语法结构为:result = <expression> ? <statement1> : <statement3>;其中,expression 是一个布尔表达式。当 expression 为真时,执行 statement1, 否则就执行 statement3。此三元运算符要求返回一个结果,因此要实现简单的二分支程序,即可使用该条件运算符。

3.2、在使用条件运算符时,还应该注意优先级问题,在编译时会出现语法错误,因为条件运算符优先于赋值运算符。

四、switch case语句

switch 语句是 Java 的多路分支语句。它提供了一种基于一个表达式的值来使程序执行不同部分的简单方法。因此,它提供了一个比一系列 if-else-if 语句更好的选择。

switch(表达式) {

    case 值1:

        语句块1;

        break;

    case 值2:

        语句块2;

        break;

    …

    case 值n:

        语句块n;

        break;

    default:

        语句块n+1;

    break;

}

其中,switch、case、default、break 都是 Java 的关键字。

Switch:表示“开关”,这个开关就是 switch 关键字后面小括号里的值,小括号里要放一个整型变量或字符型变量。表达式必须为 byte,short,int,char类型。Java7 增强了 switch 语句的功能,允许 switch 语句的控制表达式是 java.lang.String 类型的变量或表达式。只能是 java.lang.String 类型,不能是 StringBuffer 或 StringBuilder 这两种字符串的类型。

Case: 表示“情况,情形”,case 标签可以是:

        1、类型为 char、byte、 short 或 int 的常量表达式。

        2、枚举常量。

        3、从 Java SE 7 开始, case 标签还可以是字符串字面量。

default: 表示“默认”,即其他情况都不满足。default 后要紧跟冒号,default 块和 case 块的先后顺序可以变动,不会影响程序执行结果。通常,default 块放在末尾,也可以省略不写。

**注意:**重复的 case 值是不允许的。

break表示“停止”,即跳出当前结构。

        如果在 case 分支语句的末尾没有 break 语句,有可能触发多个 case 分支。那么就会接着执行下一个 case 分支语句。这种情况相当危险,常常会引发错误。为此,我们在程序中从不使用 switch 语句。

        switch 语句的执行过程如下:表达式的值与每个 case 语句中的常量作比较。如果发现了一个与之相匹配的,则执行该 case 语句后的代码。如果没有一个 case 常量与表达式的值相匹配,则执行 default 语句。当然,default 语句是可选的。如果没有相匹配的 case 语句,也没有 default 语句,则什么也不执行。

(9.6)

一、嵌套语句(switch)

可以将一个 switch 语句作为一个外部 switch 语句的语句序列的一部分,这称为嵌套 switch 语句。因为一个 switch 语句定义了自己的块,外部 switch 语句和内部 switch 语句的 case 常量不会产生冲突。例如,下面的程序段是完全正确的:

switch特点:

1、switch 语句不同于 if 语句的是 switch 语句仅能测试相等的情况,而 if 语句可计算任何类型的布尔表达式。也就是 switch 语      句只能寻找 case 常量间某个值与表达式的值相匹配。

2、在同一个 switch 语句中没有两个相同的 case 常量。当然,外部 switch 语句中的 case 常量可以和内部 switch 语句中的     case 常量相同。       

3、switch 语句通常比一系列嵌套 if 语句更有效。

二、If语句何switch语句的区别

1. 从使用效率上区分

        从使用效率上区分,在对同一个变量的不同值作条件判断时,既可以使用 switch 语句,也可以使用 if 语句。使用 switch 语句的效率更高一些,尤其是判断的分支越多,越明显。

2. 从实用性上区分

        从语句的实用性角度区分,switch 语句不如 if 条件语句,if 语句是应用最广泛和最实用的语句。

3. 何时使用 if 语句和 switch 语句

        在程序开发的过程中,何时使用 if 语句和 switch 语句,需要根据实际情况而定,应尽量做到物尽其用。不能因为 switch 语句的效率高就一直使用,也不能因为 if 语句常用就不用 switch 语句。需要根据实际情况,具体问题具体分析,使用最适合的条件语句。

        一般情况下,对于判断条件较少的,可以使用 if 条件语句,但是在实现一些多条件的判断中,最好使用 switch 语句。

三、while和do while循环

3.1、While语法:

 while循环:
         *      语法:
         *          while (循环条件) {
        *          循环体
         *          }
   do while:

         *      语法:
         *          do{
         *              循环体
         *          } while (循环条件); 

3.2 

区别: /**
         * while循环与do...while循环的区别:
         *      1、while是先判断后执行,do...while循环是先执行然后再判断
         *      2、do...while循环至少执行一次
         * while循环与do...while的特点:都是循环执行某一语句,循环次数素不固定

3.3课堂实例

public class Demo01 {
    public static void main(String[] args) {
        /**
         * while循环:
         *      语法:
         *          while (循环条件) {
         *              循环体
         *          }
         *
         *     案例:计算1+到100的和
         *     1+2+3+4+5+6+7+....+100
          */
        int num = 1;
        int sum = 0;

        while (num <= 100) {
//
            sum = sum + num;
            sum += num;

            num ++;
        }
        System.out.println(sum);


        /**
         * do.while循环
         *      语法:
         *          do{
         *              循环体
         *          } while (循环条件);
         *      案例:计算1+到100的和
         *         1+2+3+4+5+6+7+....+100
         */

        int num2 = 1;
        int sum2 = 0;

        do {
            sum2 += num2;
            num2 ++;
        } while (num2 <= 100);
        System.out.println(sum2);

        /**
         * 输出如下图形
         *             *
         *             **
         *             ***
         *             ****
         *             *****
         */
        // 记录输出的次数
        int num3 = 1;

        while (num3 <= 5) {
            if(num3 == 1){
                System.out.println("*");
            }
            if(num3 == 2){
                System.out.println("**");
            }
            if(num3 == 3){
                System.out.println("***");
            }
            if(num3 == 4){
                System.out.println("****");
            }
            if(num3 == 5){
                System.out.println("*****");
            }

            num3 ++;

        }

        /**
         * while循环与do...while循环的区别:
         *      1、while是先判断后执行,do...while循环是先执行然后再判断
         *      2、do...while循环至少执行一次
         * while循环与do...while的特点:都是循环执行某一语句,循环次数素不固定
         */

    }
}

(9.7)

一、for循环嵌套

1.1、嵌套循环既可以是 for循环嵌套 while 循环,也可以是 while 循环嵌套 do-while 循环 …… 即各种类型的循环都可以作为外层循环,也可以作为内层循环。

当程序遇到嵌套循环时,如果外层循环的循环条件允许,则开始执行外层循环的循环体,而内层循环将被外层循环的循环体来执行——只是内层循环需要反复执行自己的循环体而已。

当内层循环执行结束,且外层循环的循环体执行结束时,则再次计算外层循环的循环条件,决定是否再次开始执行外层循环的循环体。

二、foreach 语句的用法

foreach 循环语句是 Java 1.5 的新特征之一,在遍历数组、集合方面,foreach 为开发者提供了极大的方便。foreach 循环语句是 for 语句的特殊简化版本,主要用于执行遍历功能的循环。

一和二课堂实例:

public class Demo01 {
    public static void main(String[] args) {
        // 定义int类型的数组
        int[] arr = {12, 56, 78, 99, 13, 45, 48};
        // 访问数组元素:通过数组下标访问元素:如:arr[i]则表示访问数组 arr下标为i的元素
        System.out.println(arr[2]);
        // 遍历数组元素:将数组元素进行逐一的访问, arr.length获取数组的长度
        for (int index = 0; index < arr.length; index ++) {
            System.out.println(arr[index]);
        }

        /**
         * foreach语法:
         *      for(迭代变量类型 变量的名字 :需要遍历(迭代)的对象){
         *          语句块;
         *      }
         */
        int[] array = {12, 56, 78, 99, 13, 45, 48};
        for(int num : array){
            System.out.print(num + ", ");
        }
        
    }
}

三、break 语句

3.1、某些时候需要在某种条件出现时强行终止循环,而不是等到循环条件为 false 时才退出循环。此时,可以使用 break 来完成这个功能。break 用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到 break,系统将完全结束该循环,开始执行循环之后的代码。

3.2、在 Java 中,break 语句有 3 种作用,分别是:在 switch 语句中终止一个语句序列、使用 break 语句直接强行退出循环和使用 break 语句实现 goto 的功能

3.3、使用 break 语句直接强行退出循环

可以使用 break 语句强行退出循环,忽略循环体中的任何其他语句和循环的条件判断。在循环中遇到 break 语句时,循环被终止,在循环后面的语句重新开始。

3.4break 语句

某些时候需要在某种条件出现时强行终止循环,而不是等到循环条件为 false 时才退出循环。此时,可以使用 break 来完成这个功能。break 用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到 break,系统将完全结束该循环,开始执行循环之后的代码。

        在 Java 中,break 语句有 3 种作用,分别是:在 switch 语句中终止一个语句序列、使用 break 语句直接强行退出循环和使用 break 语句实现 goto 的功能

1、使用 break 语句直接强行退出循环(break 不带标签)

可以使用 break 语句强行退出循环,忽略循环体中的任何其他语句和循环的条件判断。在循环中遇到 break 语句时,循环被终止,在循环后面的语句重新开始。

方法的调用:

调用方法:对象名.方法名(实参列表),类名.方法名()。

Java支持两种调用方法的方式,根据方法是否有返回值来选择:

(1)当方法有返回值时,方法调用通常被当作一个值,例如:

int num = max(30, 40);  

(2)如果方法返回值类型是void时,方法调用一定是一条语句,例如:

System.out.println(“Hello World”);  

System.out.println( message() );  

方法重载:

重载就是在一个类中,有相同的函数名称,但参数列表不相同的方法。

方法重载的规则:

方法名字必须相同

参数列表必须不同(参数个数不同、类型不同、参数排列顺序不同等)

方法的返回值可以相同,也可以不同(方法的重载与返回值类型无关)。

仅仅只是返回类型不同的话,则不足以称为方法的重载。

方法签名:

方法名+参数列表

重载方法调用规则:

    编译器在编译时会根据方法的签名自动绑定调用的方法

算法案例:

package com.zpark.day08;

import
 java.util.Scanner;

/**

 * @PackageName: com.zpark.day08
 * @ClassName: Demo04
 * @Description:
 * @author: RZS
 * @date: 2022/9/7  9:24
 */
public
 class Demo04 {
    /**
     * 方法(函数)的类型:
     *      1、无参无返回值
     *          修饰符  void  方法名(){ 方法体 }
     *      2、无参有返回值
     *          修饰符   返回值类型   方法名(){ 方法体}
     *      3、有参无返回值
     *          修饰符   void   方法名(参数1, 参数2,....){ 方法体}
     *      4、有参有返回值
     *          修饰符   返回值类型  方法名(参数1, 参数2,....){ 方法体}
     */

    /**
     * 比较两个数的大小,将大的值返回回去
     * @param num1
     * @param num2
     * @return 返回最大值
     */
    public int max(int num1, int num2){
        /*int max = num2;
        if (num1 > num2) {
            max = num1;
        }
        return max;*/

        return num1 >= num2 ? num1 : num2;
    }

    /**
     * 定义方法:
     * 功能:
     *      比较两个数大小,返回布尔值
     */
    public boolean isMax(int n1, int n2){
        return n1 > n2 ? true : false;
    }

    /**
     * 定义方法:
     *  要求:
     *      计算用户输入i的阶层
     */
    public static long stratum(long num){
        long result = 1;
        // 判断num是否为0和1
        if (num == 0 || num == 1) {
            return result;
        }
        // num不为0和1
        for (long i = 2; i <= num; i++) {
            result *= i;
        }
        return result;
    }

    /**
     * 定义判断成绩优良中差等级的方法
     */
    public final static String grade(int score){
        String message = null;

        if (score < 0 || score > 100) {
            return "成绩有误";
        }

        if (score < 60) {
            message = "D";
        }else if(score >= 60 && score < 75){
            message = "C";
        }else if(score >= 75 && score < 85){
            message = "B";
        }else {
            message = "A";
        }
        return message;
    }

    /**
     * 方法调用
     *      1、通过 对象.方法名字(参数)
     *      2、类名.方法(参数)
     *      3、方法(参数)
     */
    public static void main(String[] args) {
        // 创建对象
        Demo04 d4 = new Demo04();
        // 调用非static关键字修饰的方法,语法:对象.方法(参数)
        int max = d4.max(3, 5);
        System.out.println(max);
        boolean b = d4.isMax(56, 78);
        System.out.println(b);

        // 调用被static修饰的方法,语法:类名.方法(参数)
        String grade = Demo04.grade(56);
        System.out.println(grade);
        long stratum = Demo04.stratum(10);
        System.out.println(stratum);

        long stratum1 = stratum(10);
        System.out.println(stratum1);

//
        int max1 = max(1, 3);
    }
    
}

package com.zpark.day08;

/**

 * @PackageName: com.zpark.day08
 * @ClassName: Demo05
 * @Description:
 * @author: RZS
 * @date: 2022/9/7  16:33
 */
public
 class Demo05 {
    public static void main(String[] args) {
        // 创建demo05对象
        Demo05 d5 = new Demo05();
        d5.fun1();
        d5.fun1(56);
        d5.fun1(3.141598);
        d5.fun1(12, 56);
    }


    /*
        方法重载:
            重载就是在一个类中,有相同的函数名称,但参数列表不相同的方法。

        注意:
            方法重载只与方法名字和参数列表有关,与方法返回值类型无关

        方法签名:方法名字 + 参数列表

        什么是程序?
        答:程序 = 算法 + 数据结构
     */  public void fun1(){
        System.out.println("我是无参无返回值的方法");
    }

    public int fun1(int num){
        System.out.println("有参无返回值的方法参数 int num");
        return 0;
    }

    public void fun1(double d){
        System.out.println("有参无返回值的方法参数 double d");
    }

    public String fun1(int number, int num){
        System.out.println("有参无返回值的方法参数 int number, int num");
        return null;
    }


}

递归算法

    是一种直接或者间接地调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。

**递归算法:**在函数或子过程的内部,直接或者间接地调用自己的算法。

**递归算法的实质:**是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。

**递归算法解决问题的特点:**

  (1) 递归就是在过程或函数里调用自身。

  (2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。

  (3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。

  (4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。

**递归的两个条件:**

        1、可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式。(自身调用)

        2、存在一种简单情境,可以使递归在简单情境下退出。(递归出口)

**递归三要素:

        1、一定有一种可以退出程序的情况;

        2、总是在尝试将一个问题化简到更小的规模

        3、父问题与子问题不能有重叠的部分

package com.zpark.day08;

/**

 * @PackageName: com.zpark.day08
 * @ClassName: Demo07
 * @Description:
 * @author: RZS
 * @date: 2022/9/7  17:19
 */
public
 class Demo07 {
    /**
     * 设计一个方法:
     *      使用递归计算i的阶层
     */
    long result = 1;
    public long stratum(long num){

        // 递归出口,当num = 0 or 1的时候,结束方法
        if (num == 0 || num == 1) {
            return result;
        }

        // result = result * stratum(num--);
        result *= num;
        num --;
        return stratum(num);
    }

    public static void main(String[] args) {
        Demo07 d7 = new Demo07();
        long stratum = d7.stratum(4);
        System.out.println(stratum);
    }
}

(9.9)

一、数组

**数组的定义:**

    相同数据类型元素的集合、是一种数据类型(引用类型)

**数组的声明:**

    首先必须先声明数组变量,然后才能在程序中使用数组,数组的定义方式有如下几种:

```java

int[ ] arr;

    或者 

int arr[ ];

``

        使用new关键字来声明数组:例如,

```java

int[ ] arr = new int[ 数组长度 ];

```

**数组元素赋值:**

```java

arr[i] = 10;

```

**访问数组:**

    数组的访问是通过数组的下标来进行访问的,数组下标(索引)从0开始

**获取数组的长度:**

```java

arr.length

```

**数组的四个基本特点:**

    1、 数组一旦被创建,它的大小将不可以在被改变。

    2、 数组元素必须是相同类型的,不允许出现混合类型。

    3、 数组中的元素可以是任何类型,包括基本类型与引用类型。

    4、 数组属于引用类型,数组可以看成是一个对象,数组中的每一个元素相当于该对象的成员变量,因为数组本身就是对象,Java中对象是存放在堆中的,因此数组无论保存原始类型还是其他类型,数组对象本身都是在堆中。

数组的下标合法区间为:`[0,length-1]`,如果在这个范围之外将会报错,这种情况称为数组下标越界。例如:

```java

public static void main(String args[]){

       int[]  arr  =  new  int[2];

// 下列代码将在运行时出现ArrayIndexOutOfBoundsException异常

       System.out.ptintln(arr[2]);

}

```

>  **注意:**

>  1、 数组是相同数据类型(数据类型可以为任意类型)的有序集合。

>  2、 数组也是对象,数组元素相当于对象的成员变量。

>  3、 数组一旦声明,长度就已经确定,不可以再被改变,如果越界,则会报:ArrayIndexOutOfBoundsException异常

算法案例:

package com.zpark.day09;


import
 java.util.Date;

public
 class Demo01 {
    public static void main(String[] args) {
        /**
         * 数组定义:
         *      用来存放相同类型的一组数据
         *      数组下标从0开始,对数组元素进行操作是通过数组的下标(索引)
         *      数组一旦被创建,则数组的长度不可被修改
         *    语法:
         *          静态创建数组
         *          数据类型[] 数组名 = {值1,值2....}
         *          数据类型 数组名[] = {值1,值2....}
         *          动态创建数组
         *          数据类型[] 数组名 = new 数据类型[数组长度]
         *          数据类型 数组名[] = new 数据类型[数组长度]
         */
        // 静态创建数组,特点:创建数组的同时给数组元素赋值
        int arr[] = {1, 2, 4, 8, 9};
        // 动态创建数组,特点创建数组时指定数组的长度
        long[] arr1 = new long[5];

        int arr2[] = new int[]{1, 5, 8, 9};

        // 数组元素赋值:语法,数组名[索引] = 
        double dArr[] = new double[3];
        // 给数组的第一个元素赋值
        dArr[0] = 3.14159;
        // 给数组的第二个元素赋值
        dArr[1] = 0.618;

        // 使用数组元素
        System.out.println(dArr[0] + 2);// 打印数组的第一个元素

        // 数组元素必须是相同类型的,不允许出现混合类型。
        // dArr[2] = "hello array"; 数组元素类型与数组类型不匹配,语法报错


        // 定义引用数据类型数组
        String[] str = new String[5];
        // 创建demo01对象数组
        Demo01[] d1 = new Demo01[10];
        Date date[] = new Date[3];

        // 数组的长度获取方式,array.length


        // 获取数组d1的长度
        int len = d1.length;
        System.out.println("数组demo01的长度为:" + len);
    }
}

9.13

二维数组:

java中使用 [][] 来定义二维数组,定义数组时也可同时初始化。

两种初始化形式:

格式1、动态初始化

数据类型 数组名[][] = new 数据类型[m][n]

数据类型[][]  数组名 = new 数据类型[m][n]

数据类型[]   数组名[] = new 数据类型[m][n]

举例:  * 二维数组:
         *      数据类型[][] 数组名字 = new 数据类型[m][n]
         *
         *      本质:数组的元素还是数组
          */
        int[][] arr = new int[2][3];
        System.out.println(arr.length);

静态初始化

数据类型[][]  数组名 = {{元素1,元素2....},{元素1,元素2....},{元素1,元素2....}.....};

举例:int[][] arr = {{22, 15, 32, 20, 18}, {12, 21, 25, 19, 33}, {14, 58, 34, 24, 66}}。

  1. 遍历数组:
  2. // 访问二维数组的元素,语法:arr[x][y]
            arr[0][0] = 10;
            arr[0][1] = 20;
            arr[0][2] = 30;

            arr[1][0] = 22;
            arr[1][1] = 32;
            arr[1][2] = 42;

            // 遍历二维数组
            for (int i = 0; i < arr.length; i++) {
                for (int j = 0; j < arr[i].length; j++) {
                    System.out.print(arr[i][j] + "\t");
                }
                System.out.println();
            }
        }
    }

二、Arrays类

由于数组对象本身并没有什么方法可以供我们调用,但是API中提供了一个根据类Arrays供我们使用,从而可以对数据对象进行一些基本操作。

        Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象类调用(注意是“不用”,而不是“不能”)。

        Arrays类具有以下常用的功能:

               给数组赋值:通过调用fill方法。

               对数组进行排序:通过sort方法。

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

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

               将数组转为字符串输出:通过toString方法实现

1

数组的复制:System.arraycopy(浅拷贝)

例子System.arraycopy(src, 1, dest, 0, 6);

*2、Arrays.copyOf(浅拷贝)

src为原数组,1为复制的起始位置,dest为目的数组,0为目的数组放置的起始位置,6为复制的长度

数组的复制:
         *      System.arraycopy(arr, start, dist, index, length)
         */
        int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

        // 案例:将arr1数组的 3-8的元素复制到arr2中去
        int arr2[] = new int[6];
        System.arraycopy(arr1, 2, arr2, 0, 6);
        for (int i : arr2) {
            System.out.print(i + "\t");
        }

        System.out.println();

        /**
         * Arrays.copyOf(arr, length)
         */
        // 案例:将arr1数组进行复制,复制的范围下标为5之前的所有元素
        int arr3[] = Arrays.copyOf(arr1, 5);
        for (int j : arr3) {
            System.out.print(j + "\t");
        }
    }
}

3、Arrays.copyOfRange**

System.out.println();
        /**
         *  Arrays.copyOfRange(src, 1, 3);
         */
        // 案例:将arr1数组中的元素复制到arr4数组中,复制到范围为下标1开始,到下标6结束
        int[] arr4 = Arrays.copyOfRange(arr1, 1, 6);
        for (int i : arr4) {
            System.out.print(i + "\t");
        }

、数组扩容与缩容:

        数组扩容与缩容的实质:就是创建一个新的数组,新数组的长度比原来的数组(大,扩容,小,缩容),然后将原来数组中的内容全部拷贝到新的数组中,最后将新数组重新赋给原来的数组。

举例:

 public static void main(String[] args) {
        /**
         * 数组扩容与缩容的实质:就是创建一个新的数组,新数组的长度比原来的数组(大,扩容,小,缩容)
         */
        int arr[] = new int[10];
        // 创建随机数对象
        Random rand = new Random();
        for (int i = 0; i < 5; i++) {
            arr[i] = rand.nextInt(100);
        }

        // 打印数组,Arrays.toString(arr)方法的作用是将数组以字符串的形式输出
        System.out.println(Arrays.toString(arr));

        // 对数组进行缩容
        int arr1[] = Arrays.copyOf(arr, 5);
        System.out.println(Arrays.toString(arr1));

        // 扩容,案例:将arr数组长度扩容到15
        int[] arr2 = Arrays.copyOf(arr, 15);
        System.out.println(Arrays.toString(arr2));

    }
}

二、冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。### 7.2.1、算法描述 1、比较相邻的元素。如果第一个比第二个大,就交换它们两个; 2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; 3、针对所有的元素重复以上的步骤,除了最后一个; 4、重复步骤1~3,直到排序完成。

public static void main(String[] args) {

        int arr[] = {6,9,3,5,2,8};

        System.out.println("排序前:");

        System.out.println(Arrays.toString(arr));

        for(int i =0;i<arr.length-1;i++){

            for(int j=0;j<arr.length-1-i;j++){

                int tamp = 0;

                if (arr[j]>arr[j+1]) {

                    tamp = arr[j];

                    arr[j] = arr[j+1];

                    arr[j+1] = tamp;

                }

            }

        }

        System.out.println("排序后:");

        System.out.println(Arrays.toString(arr));

    }

}

三、选择排序

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

(9.14)

一、面向对象

面向过程思想:

               1、 步骤清晰简单,第一步要做什么,第二步要做什么……

               2、 面向过程适合处理一些较为简单的问题

面向对象思想:

1、 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类      下的细节进行面向过程的思索。

2、 面向对象适合处理复杂的问题,适合处理需要多人协作的问题

面向过程的结构化程序设计:

结构化程序设计的弊端:

        1、缺乏对数据的封装

        2、数据与方法(操作数据)的分离

类是对象的抽象,对象都是类的属性.

2什么是面向对象:

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

3、类中可以包含:

               3.1、所有对象所共有的属性/特征------------成员变量

               3.2、所有对象所共有的行为-----------------方法

        4、一个类可以创建多个对象

               同一类型所创建的对象,结构相同,数据不同

  1. 类是对象的模板,对象是类的具体的实例
  2. 面向对象的三大特征:封装、继承、多态

所谓的OOP就是指的面向对象编程.这是一种计算机编程架构.OOP的一条基本准则是计算机程序是由单个能够引

 * 起子程序作用的单元或对象组合而成.OOP达到了软件工程的三个主要目标:重用性,灵活性和扩展性.为了实现

 * 整体运算,每个对象都能够接收消息,处理数据和向其它对象发送消息.OOP主要有以下的概念和组件.

 *

 * 组件: 数据和功能一起在运行着的计算机程序中形成的单元,组件在OOP计算机称重是模块和结构化的基础.

 *

 * 抽象性: 程序有能力忽略正在处理中信息的某些方面,即对信息主要方面关注的能力

 *

 * 封装: 也叫作信息封装:确保组件不会以不可预期的方式改变其它组件内部状态;只有在那些提供了内部状态改变方法的

 *

 * 组建中,才可以访问其内部状态.每类组件都提供了一个与其它组件联系的接口.并规定了其它组件进行调用的方法.

 *

 * 多态性: 组件的引用和类集会涉及到其它许多不同类型的组件,而且引用组件所产生的的结果得依据实际调用的类型.

 *

 * 继承性: 允许在现存的组件基础上创建子类组件,着统一并强调了多态性和封装性.典型的来说就是用类来对组件进行分

 * 组,而且还可以定义新类为现存的类的扩展,这样就可以将类组织成树形或网状结构,这体现了动作的通用性.

 *

 *      由于抽象性/封装性/重用性以及便于使用等方面的原因,以组件为基础的编程在脚本语言中已经变得特别

 * 流行.Python 和 Ruby 是最近才出现的语言,在开发时完全采用了 OOP 的思想,而流行的 Perl 脚本

 * 语言从版本5开始也慢慢地加入了新的面向对象的功能组件。用组件代替“现实”上的实体成为 JavaScript(ECMAScript)

 * 得以流行的原因,有论证表明对组件进行适当的组合就可以在英特网上代替 HTML 和 XML 的文档对象模型(DOM)。

二、类与对象的创建

 */

public class Student {

    // 内部类

    private class C{

    }

}

// 定义类A

class A{

}

// 注意:一个java文件中只能有一个public修饰的Java类,并且这个被public修饰的Java类名必须与Java文件名字一致

//public class B{

//

//}

课堂实例:

// 定义成员变量
    String name; // 姓名
    int age; // 年龄
    double height; // 身高
    // ....

// 定义方法

    public void run(){

        System.out.println("人在跑");

    }

    public void eat(){

        System.out.println("人在吃饭");

    }

    // 将数组排序

    public int[] sort(int[] arr){

        Arrays.sort(arr);

        return arr;

    }

    @Override

    public String toString() {

        return "Person{" +

                "name='" + name + '\'' +

                ", age=" + age +

                ", height=" + height +

                '}';

    }

}

6.2创建对象:

/**

         * 创建对象语法:

         * 数据类型   引用类型变量   指向(引用)  new关键字        对象

         * Student       zs         =      new           Student();

         */

        // 案例:创建Person对象

        Person per = new Person(); // 实例化对象

        // 访问对象的成员变量和方法,语法:对像名.成员变量名字/方法名字, 能点出什么东西全看类有什么东西

        // 访问person类的成员变量

        per.name = "大锤"; // 访问name变量并且赋值

        // per.salary = 3000; 错误演示

        System.out.println(per.toString());

        per.age = 25;

        per.height = 177.777;

        System.out.println(per.toString());

        // 访问变量

        System.out.println(per.name);

        // 访问成员方法

        per.run();

        int[] ar = {1, 56, 0, 89, 78, 45};

        int[] arr = per.sort(ar);

        System.out.println(Arrays.toString(arr));

        // 实例化对象p2

        Person p2 = per; // 将per的值(对象地址信息)赋给p2,此时per和p2指向相同对象

        System.out.println(p2.toString());

        Person p3 = new Person();

        System.out.println(p3.toString());

        Person p4 = null;

        /**

         * Exception in thread "main" java.lang.NullPointerException

         * 空指针异常:

         *      造成空指针异常的原因:是因为对象为null,然后访问其对象变量或者方法

         */

        // System.out.println(p4.toString());

    }

}

二、面向对象分析产生三种模型

        1对象模型:对用例模型进行分析,把系统分解成互相协作的分析类,通过类图\对象图描述对象\对象的属性\对象间的关系,是系统的静态模型

        2动态模型:描述系统的动态行为,通过时序图/协作图/描述对象的交互,以揭示对象间如何协作来完成每个具体的用例。单个对象的状态变化/动态行为可以通过状态图来表示。

        3功能模型(即用例模型à作为输入)

(9.19)

三、构造方法

构造方法语法结构:

        构造方法是在类中定义的方法,不同于其他的方法,构造方法的定义有如下两点规则:

               1)构造方法的名称必须与类名完全相同。

               2)构造方法没有返回值,连void关键字有没有

构造方法的语法:

     *              修饰符   类名(参数){

     *                 方法体....

     *              }

     *      构造方法的作用:用于创建类的对象(实例化对象),还可以初始化成员比啊量的初始值

     *

     *      注意:当我们手动添加构造器(构造方法)之后,编译器将不在为我们添加默认的无参构造器

默认的构造方法:

  1)任何一个类都必须含有构造方法。

  2)如果源程序中没有定义构造方法,编译器在编译时会为其添加一个无参的构造方法(称为默认无参构造器)。

  3)当定义了构造方法之后,Java编译器不再添加默认的无参构造器。

构造方法的重载:

  一个类定义多个构造方法,这些构造方法都有相同的名称,但是方法的参数列表不同

一个构造方法可以通过this关键字调用另外一个重载的构造方法

构造方法常用于实例化对象和对成员变量进行初始化

this关键字的使用:

this关键字用在方法体中,用于指向调用该方法的当前对象,简单来说,那个对象调用方法,this就指的是那个对象,严格来讲在方法中需要通过this关键字指明当前对象。

实例:

int age;

    String name;

    double salary;

    // 通过构造器初始化成员变量的值

    public Demo01(){

        age = 23;

        name = "大锤";

        salary = 3000.0;

    }

    // 定义有参构造器

    public Demo01(int age){

        System.out.println("我是有参构造器");

    }

    public Demo01(int age, String name, double salary){

        // this关键字,代表当前对象

        this.age = age;

        this.name = name;

        this.salary = salary;

    }

    @Override

    public String toString() {

        return "Demo01{" +

                "age=" + age +

                ", name='" + name + '\'' +

                ", salary=" + salary +

                '}';//右击

    }

}

Java内存分析

堆: new出来的对象(包括实例变量)

栈:局部变量(包括方法的参数)

方法区: .class字节码文件(包括方法、静态变量)

内存区域类型

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制;

2. 堆:存放所有new出来的对象;

3. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。);

4. 静态域:存放静态成员(static定义的);

5. 常量池:存放字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中 ;

6. 非RAM存储:硬盘等永久存储空间

对于string的特殊解释

对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。

对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。

    这也就是有道面试题:String s = new String(“xyz”);产生几个对象?答:一个或两个,如果常量池中原来没有”xyz”,就是两个。

例如:

1) 堆:

    1.1) 存储new出来的对象(包括实例变量)

    1.2) 垃圾: 没有任何引用所指向的对象

        垃圾回收器(GC)**不定时到内存中清理垃圾,

        回收过程是透明的(看不到的),不一定一发现垃圾就立刻回收,

        调用System.gc()可以建议虚拟机尽快调度GC来回收

    1.3) 内存泄漏: 不再使用的内存没有被及时的回收

        建议: 对象不再使用时及时将引用设置为null

    1.4) 实例变量的生命周期:

        在创建对象时存储在堆中,对象被回收时一并被回收

2) 栈:

    2.1) 存储正在调用的方法中的所有局部变量(包括方法的参数)

    2.2) 调用方法时,会在栈中为该方法分配一块对应的栈帧,

        栈帧中存储方法中的局部变量(包括参数),

        方法调用结束时,栈帧被清除,局部变量一并被清除

    2.3) 局部变量的生命周期:

    方法被调用时存储在栈中,方法结束时与栈帧一并被清除

3) 方法区:

    3.1) 存储.class字节码文件(包括静态变量、方法)

    3.2) 方法只有一份,通过this来区分具体的调用对象

对象数组

数组是对象:

    1、在Java中,数组属于引用数据类型。

    2、数组对象存储在堆中,数组变量属于引用类型,存储数组对象的地址信息,指向数组对象。

数组的元素可以看成数组对象的成员变量(只不过类型全部相同)

public class Demo02 {

    public static void main(String[] args) {

        // 定义一个数据类型,用于存放Student信息

        Student[] stu = new Student[48];

        // 创建student对象

        Student zs = new Student("张三", 23, "202110107890");

        // 把张三对象装到数组中

        stu[0] = zs;

        stu[1] = new Student("李四", 21, "202145678956");

        Demo01 d1 = new Demo01();

//        stu[2] = d1; 报错,数据类型不匹配

        System.out.println(stu[0]);

        // 案例:定义一个数组,可以存放任意数据类型

        Object[] obj = new Object[3];

        obj[0] = 12;

        obj[1] = stu;

        obj[2] = "hello";

        Student[] students = {new Student(), new Student(), new Student()};

访问控制修饰符

private修饰的成员变量和方法仅仅只能在本类中访问调用。

public修饰的成员变量和方法可以在任何地方调用,public修饰的内容是对外提供可以被调用的功能,需要相对稳定,private修饰的内容是对类实现的封装,如果“公开”会增加维护的成本。

protected和默认访问控制

    1)用protected修饰的成员变量和方法可以被子类及同一个包中的类使用。

    2)默认的访问控制即不书写任何访问修饰符,默认访问控制的成员变量和方法可以被同一个包中的类调用

package com.zpark.oop.day03;

public class Demo04 {

    /**

     * private:私有的

     *      特点:凡是被private关键字修饰的属性和方法,只能在本类了当中访问,在其他类当中访问则没有权限

     *

     * public:关键字

     *      特点:任何地方都可以访问

     */

    public String nickname; // 昵称

    private String name; // 姓名

    protected int age;

    double salary; // 当不添加任何修饰符时,则为默认访问修饰符default

    public void a(){

        System.out.println("这是公共的方法");

    }

    private void b(){

        System.out.println("这是私有的方法");

    }

    public static void main(String[] args) {

        // 创建Demo04对象

        Demo04 d4 = new Demo04();

        // 访问d4对象的公有属性和方法

        d4.nickname = "大锤";

        System.out.println(d4.nickname);

        d4.a();

        // 访问d4对象的私有属性和方法

        d4.name = "张无忌";

        System.out.println(d4.name);

        d4.b();

    }

}

public class Demo05 {

    public static void main(String[] args) {

        // 创建Demo04对象

        Demo04 d4 = new Demo04();

        // 访问d4对象的公有属性和方法

        d4.nickname = "大锤";

        System.out.println(d4.nickname);

        d4.a();

        // 访问d4对象的私有属性和方法

        // d4.name = "张无忌"; 不能访问其他类中的私有属性

        // System.out.println(d4.name);

        // d4.b(); 不能访问其他类中的私有方法

        // 访问默认修饰符的属性

        d4.age = 23;

        // 访问默认修饰符的属性

        d4.salary = 5000.0;

        // 访问默认修饰符修饰的类

        A a = new A();

        System.out.println(a.name);

    }

}

package com.zpark.oop.day03;

public class Student {

    String name; // 姓名

    int age; // 年龄

    String number; // 学号

    public Student(){

        // 无参构造器

    }

    public Student(String name, int age, String number) {

        this.name = name;

        this.age = age;

        this.number = number;

    }

    @Override

    public String toString() {

        return "Student{" +

                "name='" + name + '\'' +

                ", age=" + age +

                ", number='" + number + '\'' +

                '}';

    }

}

封装

封装,简单的说就是该露的露,该藏的藏。我们在设计程序是要追求“高内聚,低耦合”,其中,高内聚指的是类的内部数据操作细节由自己完成,不允许外部干涉。低耦合指的是仅暴露少量的方法给外部调用(使用get/set方法)。

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

封装的意义:

    1、对外提供可调用的,稳定的功能。

    2、封装容易变化的,具体的细节,外界不可访问,这样封装的意义在于:

     a. 降低代码出错的可能性,便于维护。

    b. 当内部的实现细节改变时,只要保证对外的功能定义不变,其他的模块就不会因此而受到牵连。

封装的核心:属性私有化,行为(方法)公开化

继承

1、通过extends关键字可以实现类的继承。

2、子类可以继承父类的成员变量及成员方法,同时也可以定义自己的成员变量和成员方法。

3、Java语言不支持多重继承,一个类只能继承一个父类,但是一个父类可以有多个子类。

1、子类的构造方法中必须通过super关键字调用父类的构造方法,这样可以妥善的初始化继承自父类的成员变量。

2、如果子类的构造方法中没有调用父类的构造方法,Java编译器会自动的加入对父类的无参构造方法的调用(如果父类没有无参构造方法,则会有编译错误)。

super关键字

在java里面,对于super关键字通常有两种用法:**

 1. 用在子类的构造方法里(初始化用),主要是调用父类的默认构造方法,如果父类有不止一个构造方法,可以通过super指定具体的构造函数,比如 super(paras)。

  2. 用在子类里调用隐藏或重写的属性或行为,比如 super.onDestroy()等等。

对于第1种需要注意,super表示当前类的父类对象,super()调用的是父类默认的构造方法,即这样可以对父类进行初始化。如果没有对父类进行初始化,当子类调用父类的方法时,便会从逻辑上出现错误,因为没对父类初始化,父类的方法和属性便没有内存空间。

 关于super 与 this 关键字的对比(区别):**

  1. super(参数):调用基类中的某一个构造函数(应该位于子类构造函数中的第一条语句)。

  2. this(参数):调用本类中的构造函数(应该位于构造函数中的第一条语句)。

 3. super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)。

 4. this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)

  5、调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。

  6、super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它构造方法。

  7、super()和this()均需放在构造方法内第一行。

  8、尽管可以用this调用一个构造器,但却不能调用两个。

 9、this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

 10、 this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。

 11、 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

十方法重写

方法的重写(Override):

1、发生在父子类中,方法名称相同,参数列表相同,方法体不同

2、重写方法被调用时,看对象的类型

    3、遵循"两同两小一大"原则:------------了解

3.1、两同:

        3.1.1、方法名称相同

        3.1.2、参数列表相同

    3.2、两小:

        3.2.1、派生类方法的返回值类型小于或等于超类方法的

           a、void时,必须相同

           b、基本数据类型时,必须相同

           c、引用数据类型时,小于或等于

        3.2.2、派生类方法抛出的异常小于或等于超类方法的

    3.3、一大:

        3.3.1、派生类方法的访问权限大于或等于超类方法的

重写与重载的区别:

1、重写(Override):

        1.1、发生在父子类中,方法名相同,参数列表相同,方法体不同

        1.2、"运行期绑定",看对象的类型绑定方法

2、重载(Overload):

        2.1、发生在一个类中,方法名相同,参数列表不同,方法体不同

        2.2、"编译期绑定",看参数/引用的类型绑定方法

补充:

    编译期:.java源文件,经过编译,生成.class字节码文件

    运行期:JVM加载.class并运行.class

    编译错误:只是检查语法

十一、多态

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

        一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多。

        多态存在的条件:

               1、 有继承关系

               2、 子类重写父类的方法

               3、 父类引用指向子类对象

多态的意义:

        1、行为的多态(所有抽象方法都是多态的)

        2、对象的多态(所有对象都是多态的)

多态的表现形式:

        1、重写:根据对象的不同来表现多态

        2、重载:根据参数的不同来表现多态

注:多态是方法的多态性,属性没有多态性。

十二、instanceof 和类型转换

        instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。

        instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例(实例指的是“类”在实例化之后叫做一个“实例”。实例化:通常把用类创建对象的过程称为实例化),返回 boolean 的数据类型。

向上造型:

        1)超类型的引用指向派生类的对象

        2)能点出来什么,看引用的类型

向下造型:(容易出现类型转换异常)

        1)派生类的引用指向超类对象

        2)向下造型需要进行强转

        3)能点出来什么,看引用的类型

(9.26)面向对象 六大设计原则简介

一、设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任键模式、合令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。
二、设计模式的六大原则

1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级,想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov
 Substitution Principle)
里氏代换原则(Liskoy Substitution Principle LSP)面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当初生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化,直基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。——From Baidu百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface
 Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低耦合。
5、迪米特法则(最少知道原则)
 (Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。

十三、static和final关键字

1、static修饰成员变量

1、用static修饰的成员变量不属于对象的数据结构,属于类的数据结构。

2、static变量是属于类的变量,通常可以通过类名来引用static成员。

3、static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static变量只有一份,无论这个类创建了多少个对象。

2、static修饰方法

1、通常的方法都会涉及到对具体对象的操作,这些方法在调用时需要隐式传递对象的引用(this)。

2、static修饰的方法则不需要针对某些对象进行操作,其运行结果仅仅与输入的参数有关,调用时直接用类名引用。

3、static在调用时没有具体的对象,因此在static方法中不能对非static成员进行访问,static方法的作用在于提供一些“工具方法”和“工厂方法”等。

3、static静态块

Static块属于类的代码块,在类加载期间指向代码块,只执行一次,可以用来在软件中加载静态资源。

课堂实例:

public class Demo01 {

    public static String name = "张三";

    public String name1 = "张三";

    public static void run(){

        System.out.println("我是静态方法");

    }

    public static void main(String[] args) {

        // 实例化Demo01对象

        Demo01 d1 = new Demo01();

        Demo01 d2 = new Demo01();

        Demo01 d3 = new Demo01();

        // 不推荐使用对象的方式访问静态成员和方法

        d1.name1 = "李四1";

        d2.name1 = "李四2";

        d3.name1 = "李四3";

        Demo01.name = "王五";

        run();

        System.out.println(d1.name);

        System.out.println(d2.name);

        System.out.println(d3.name);

    }

}

public class Demo02 {

    public static void main(String[] args) {

        // 访问Demo01里面的run方法

        Demo01.run();                          

    }

}

public class Demo03 {

    public static String name;

    public String username;

    // 成员方法

    public void a(){

        name = "李四";

        b();

    }

    // 定义类的方法

    public static void b(){

        // username = "大锤";

//        a();

    }

}

public class Demo04 {

    /**

     * Static块属于类的代码块,在类加载期间指向代码块,只执行一次,可以用来在软件中加载静态资源。

     * 语法:

     *      static {

     *          静态代码块

     *      }

     */

    static {

        System.out.println("游戏静态资源被加载(图片,音频...)");

    }

    public static void main(String[] args) {

    }

}

public class Demo05 {

    public static void main(String[] args) {

        // 实例化demo04对象

        Demo04 d1 = new Demo04();

        Demo04 d2 = new Demo04();

        Demo04 d3 = new Demo04();

        Demo04 d4 = new Demo04();

        Demo04 d5 = new Demo04();

        Demo04 d6 = new Demo04();

        Demo04 d7 = new Demo04();

    }

}

4、final修饰变量

1、final关键字修饰成员变量,表示变量不可被改变。

2、final修饰成员变量的两种方式初始化:

a. 声明的同时初始化

b. 构造函数中初始化

3、final关键字也可以修饰局部变量,使用之前初始化即可

5、final修饰方法

1、final关键字修饰的方法不可被重写。

2、使一个方法不能被重写的意义在于:防止子类在定义新方法使造成“不间意”重写。

6、final修饰类

1、final关键字修饰的类不可被继承。

2、 JDK中的一些基础类库被定义为final的,例如:String、Math、Integer、Double等。

3、使一个类不能继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害。

7、static final常量

1、static final修饰的成员变量称为常量,必须声明同时初始化,不可被改变。

2、static final常量会在编译期被替换

public class Demo06 {

    // 定义常量

    public final String name = "大锤";

    public final String pwd;

    public Demo06() {

        pwd = "123456";

    }

    public void eat(){

        final int age = 20;

        final double pi;

        pi = 3.1415926;

        // 修改final修饰的变量

        // age = 56;

        // name = "王大锤";

        System.out.println(pi);

    }

}

public final class Demo07 {

    public final void eat(){

        System.out.println("Demo07里面的eat方法");

    }

}

public class Demo08 /*extends Demo07*/{

//    public void eat(){

//

//    }

}

public class Demo09 {

    public static final String NAME = "大锤";

    public final static String PWD = "123456";

//    public static final String NICK;

    public Demo09() {

        NICK = "张三";

    }

十四、抽象类(abstract)(9.27)

抽象类描述:(在Java语言中使用abstract class来定义抽象类)

        在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

        抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

        由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。

        父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。

        在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。

抽象方法:**

        1、由abstract修饰

        2、只有方法的定义,没有具体的实现(连{ }都没有)

        如果一个方法使用 abstract 来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是 abstract 关键字只能用于普通方法,不能用于 static 方法或者构造方法中。

        抽象方法的 3 个特征如下:

               1)抽象方法没有方法体

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

               3)子类重写父类时,必须重写父类所有的抽象方法

        注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。

抽象类:

        1、由abstract修饰2A

        2、包含抽象方法的类必须是抽象类

        不包含抽象方法的类也可以声明为抽象类------

        3、抽象类不能被实例化

        4、抽象类是需要被继承的,派生类:

        4.1、重写所有抽象方法--------常用

        4.2、也声明为抽象类----------一般不这么做

        5、抽象类的意义:

               5.1、封装派生类所共有的属性和行为---------代码复用

               5.2、给所有派生类提供统一的类型-----------向上造型

               5.3、可以包含抽象方法,为所有派生类提供统一的入口

                       派生类的具体行为不同,但入口是一致的

设计规则:

        1、将派生类所共有的属性和行为,抽到超类中--------抽共性

        2、所有派生类的行为都一样,设计普通方法

               所有派生类的行为都不一样,设计为抽象方法

课堂实例:

public abstract class Demo01 {

    // public abstract Demo01();abstract关键字不能修饰构造器

    String name;

    public Demo01() {

    }

    public void a(){

    }

    /**

     * 抽象方法使用abstract关键字修饰:

     *      抽象方法没有方法体,连{}也没有

     *

     *      抽象方法允许有方法重载

     *

     *   注意:如果一个类包含抽象方法,那么该类必须声明为抽象类

     */

    public abstract void b();

    public abstract void b(int age);

    public void b(String name){

    }

    // public static abstract void c(); abstract关键字不能修饰static的方法

    // private abstract void d();abstract关键字不能修饰private的方法

}

public class Demo01Test {

    public static void main(String[] args) {

        // 创建demo01的对象

//        Demo01 d1 = new Demo01(); 抽象类不允许实例化

        // 创建Demo02对象

        Demo02 d2 = new Demo02();

        d2.name = "大锤";

        d2.a();

        d2.b();

    }

}

public class Demo02 extends Demo01{

//由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用

    @Override

    public void b() {

    }

    @Override

    public void b(int age) {

    }

}

/**

 * 声明一个抽象类

 *      但是该类当中没有抽象方法

 */

public abstract class Demo03 {

}

public abstract class Person {

    public abstract void feelingOfHeartbeat(String name);

}

public class XH extends Person{

    @Override

    public void feelingOfHeartbeat(String name) {

        System.out.println("当小红遇见自己心动男神" + name + "的时候,激动不已");

    }

}

public class ZS extends Person{

    @Override

    public void feelingOfHeartbeat(String name) {

        System.out.println("当张三遇见自己心动女神" + name + "的时候,心跳加速");

    }

}

public class Demo04 {

    public static void main(String[] args) {

        // 实例化zs、xh

        Person zs = new ZS();

        zs.feelingOfHeartbeat("柳岩");

        Person xh = new XH();

        xh.feelingOfHeartbeat("张翰");

    }

}

十五、接口(Interface)(9.27)

        接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过实现(implements)接口的方式,从而来实现接口的抽象方法。

        接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

        除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

        接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

接口与类相似点:

        1)一个接口可以有多个方法。

        2)接口文件保存在 .java 结尾的文件中,文件名使用接口名。

        3)接口的字节码文件保存在 .class 结尾的文件中。

        4)接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:

        1)接口不能用于实例化对象。FDS

        2)接口没有构造方法。

        3)接口中所有的方法必须是抽象方法。

        4)接口不能包含成员变量,除了 static 和 final 变量。

        5)接口不是被类继承了,而是要被类实现。

        6)接口支持多继承(接口不能继承类,接口只能继承接口)。

接口特性:

        1)接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。

        2)接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。

        3)接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别:

        1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

        2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

        3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。

        4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

注:JDK 1.8 以后,接口里可以有静态方法和方法体了。

课堂实例:

public interface Demo05 {

//    public Demo05(); 接口中不能有构造器

    // 在接口中定义的变量默认为 public final static修饰

    public final static String NAME = "大锤";

    // 接口中的方法必须全部是抽象方法

    public abstract void a();

    int b();

}

public class Demo06 {

    public static void main(String[] args) {

        // 实例化Demo07

        Demo07 d7 = new Demo07();

        d7.a();

        // 访问接口中的常量 实现类名.常量名

        System.out.println(Demo07.NAME);

        // 访问接口中的常量 接口名.常量名

        System.out.println(Demo05.NAME);

        // 接口不能被实例化

        // Demo05 d5 = new Demo05();

    }

}

public class Demo07 implements Demo05 {

    @Override

    public void a() {

    }

    @Override

    public int b() {

        return 0;

    }

}

/**

 * 实现接口

 *      1、重写接口中的所有方法

 *      2、将当前类声明为抽象类

 */

public abstract class Demo08 implements Demo05{

}

public interface Inter01 {

    String a();

}

public interface Inter02 {

    int b();

}

/**

 * 接口不能实现接口

 *

 * 但是接口可以继承接口

 */

public interface Inter03 extends Inter01, Inter02 {

//    static {

//        接口中不允许有静态代码块

//    }

    // JDK1.8以后可以允许有静态方法和方法体

    public static void myMethod() {

        System.out.println("我是接口当中的静态方法");

    }

    double c();

}

/**

 * 接口多实现

 */

public class Demo09 implements Inter01, Inter02{

    @Override

    public String a() {

        return null;

    }

    @Override

    public int b() {

        return 0;

    }

}

public class Demo10 implements Inter03{

    @Override

    public String a() {

        return null;

    }

    @Override

    public int b() {

        return 0;

    }

public class Demo11 {

    public static void main(String[] args) {

        // 访问接口中的静态方法

        Inter03.myMethod();

    }

}

内部类

java内部类的几种类型:成员内部类,静态内部类,方法内部类,匿名内部类。

成员内部类:

成员内部类是类内部的非静态类。成员内部类不能定义静态方法和变量(final修饰的除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。

成员内部类的使用方法

1、 Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等

2、 Inner 类中定义的 test() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,如直接访问 Outer 类中的私有属性a

3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );

静态内部类:

静态内部类是 static 修饰的内部类,这种内部类的特点是:

1、 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问。

2、 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与      内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员。

3、 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();

方法内部类(局部内部类):

方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。

需要注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。

匿名内部类:

    匿名类是不能有名称的类,所以没办法引用他们。必须在创建时,作为new语句的一部分来声明他们。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。

    这种形式的new语句声明一个 新的匿名类,他对一个给定的类进行扩展,或实现一个给定的接口。他还创建那个类的一个新实例,并把他作为语句的结果而返回。要扩展的类和要实现的接口是 new语句的操作数,后跟匿名类的主体。

    注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着 for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。

    从技术上说,匿名类可被视为非静态的内 部类,所以他们具备和方法内部声明的非静态内部类相同的权限和限制。

    假如要执行的任务需要一个对象,但却不值得创建全新的对象(原因可能 是所需的类过于简单,或是由于他只在一个方法内部使用),匿名类就显得很有用。匿名类尤其适合在Swing应用程式中快速创建事件处理程式。

public class Demo01 {

    public static void main(String[] args) {

        // 常见外部类对象

        Outer outer = new Outer();

        // 创建内部类的对象  语法: 外部类.内部类 = 外部类对象.new 内部类()

        Outer.Inner inner = outer.new Inner();

        inner.uname = "";

        inner.a();

    }

}

public class Demo02 {

    public String name;

    public static int age;

    // 定义静态内部类

    public static class Aoo{

        public String uname;

        public static int age;

        public static void a(){

            System.out.println("我是静态内部类的静态A方法");

            // 访问外部静态变量

            Demo02.age = 56;

            age = 56;

            // 访问外部类的成员变量

            new Demo02().name = "";

        }

    }

}

public class Demo03 {

    public void eat(){

        // 定义方法内部类, 不能对方法内部类使用访问修饰符和static关键字

        class Aoo{

            public String name;

        }

        Aoo a = new Aoo();

        a.name = "大锤";

    }

}

public class Demo06 {
    public static void main(String[] args) {
        // 匿名内部类
        new Demo04(){
 
            @Override
            public void b() {
 
            }
        };
 
        new Inter01(){
 
            @Override
            public void a() {
 
            }
        };
 
        new Demo05(){
 
            @Override
            public void a() {
 
            }
 
            @Override
            public void b() {
 
            }
        };
 
 
        new Runnable(){
 
            @Override
            public void run() {
 
            }
        };
 
    }
}

public class Outer {

    public String name;

    public static int age;

    private boolean bool;

    public void a(){

        System.out.println("我是外部类的成员a方法");

    }

    public static void b(){

        System.out.println("我是外部类定义的静态b方法");

    }

    public void d(){

        // 创建内部类实例

        Inner inner = new Inner();

    }

    // 定义成员内部类

    /**

     * 语法:

     *      修饰符  class关键字 类名{类体}

     *

     *  内部类的作用通常用于服务外部类

     */

    public class Inner{

        public String uname;

        // public static double salary; 内部类不允许定义静态的变量

        public void a(){

            System.out.println("我是内部类Inner的成员a方法");

            System.out.println("外部类私有属性输出:" + bool);

            // 访问外部类静态变量和方法

            Outer.age = 23;

            Outer.b();

        }

        // public static void b(){ } 内部类不允许定义静态的方法

    }

    class Aoo{

    }

    private class Boo{

    }

}

枚举

​    枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在曰常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是 7 天中的一个等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。

    在 JDK 1.5 之前没有枚举类型,那时候一般用接口常量来替代。而使用 Java 枚举类型 enum 可以更贴近地表示这种常量。

基于已有知识进行答案猜测的一种问题求解策略。

从可能的集合中一一列举各元素 根据所知道的知识,给一个猜测的答案,比如:2是素数 枚举算法: 对问题可能解集合的每一项 根据问题给定的检验条件判定哪些是成立的 使条件成立的即是问题的解 枚举过程: 判断猜测的答案是否正确 2是小于N的最大素数么? arr[flag] = tmp; flag++; size--; } return arr; } } 42 43 44 45 46 47 48 进行新的猜测:有两个关键因素要注意 猜测的结果必须是前面的猜测中没有出现过的,每次猜测的素数一定比已经找到的素数 大 猜测的过程中要及早排除错误的答案。除2之外,只有奇数才可能是素数

声明枚举

声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。

    任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。

提示: 如果没有显式地声明基础类型的枚举,那么意味着它所对应的基础类型是 int。

例 1

下面代码定义了一个表示性别的枚举类型 SexEnum 和一个表示颜色的枚举类型 Color。

   

public enum SexEnum{

    male,female;

}

public enum Color{

    RED,BLUE,GREEN,BLACK;

}

枚举类

    Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可。

为枚举添加方法

Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例。

public enum Demo09 {

    QIAO_FENG("乔峰","降龙十八掌"),

    DUAN_YU("段誉","六脉神剑"),

    XU_ZHU("虚竹","小无相功");

    private final String NAME;

    private final String DESC;

    Demo09(String NAME, String DESC) {

        this.NAME = NAME;

        this.DESC = DESC;

    }

}

public class Demo09Test {

    public static void main(String[] args) {

        Demo09 d1 = Demo09.QIAO_FENG;

    }

}

public void testFun02(){

for(int x=0; x<=100; x++){

for(int y=0; y<=100-x; y++){

int z = 100 - x - y;

if(z % 3 == 0){

if(5*x + 3*y + z/3 == 100)

System.out.println("鸡翁 " + x + "只,鸡母 " + y + "只,鸡

雏 " + z + "只!");

}

}

}

}

public class TestEnum{

   

    public enum Sex{

        //定义一个枚举

        male,female;

    }

   

    public static void main(String[] args){

        compare(Sex.valueOf("male"));    //比较

    }

   

    public static void compare(Sex s){

        for(int i=0;i<Sex.values().length;i++){

            System.out.println(s+"与"+Sex.values()[i]+"的比较结果是:"+s.compareTo(Sex.values()[i]));

        }

    }

}

1、String字符串处理

  • Java定义字符串
    1
    字符串是Java中特殊的关,使用方法像一般的基本数据类型,被广泛应用在java编程中, java没有内置的字符串类型,而是在标准Java类库中提供了一个String关来创建和操作字符串。
    在java中定义一个学特用品筒中的方法是用双引号把它包围起来。这种用双引号提出来的一串字符实际上都是String对象,如字符串“Hello”在端译后面成为String对象,因此也可以通过创建String类的实例来定义字符串。
  1. 直接定义字符串
    血液定义字将单是物使用双引导的、物串中的内容,例如“Hello jaw”、“Java输配”物,具体方法是用字符串剂量直接切处化一个String对象,示例如下:
    String str="Hello 2ava":

String(char[]value)
分配一个新的字符串,将参数中的字符数组元素全配交为字符串。该字符数组的内容已被复制,后续对字符数组的修改不会影响新创建的字符

sChar变革的值是字符串“Hello”。切便在创建字符串之后,对a数组中的第2个元素进行了修改,但未影响sChar的值。

4. String(char[]value. int offset, int count)
分配一个新的string,它包含来自 答数组参数一个子数组的字符。offset参数身子数组第一个字符的末引, count参数指定子数组的长度。该子数组的内容已被赋值。 “数组的修改不会影响新创建的字符串。例如:
char a□=(&apos;W&apos;,&apos;e&apos;,&apos;1&apos;,&apos;T&apos;,&apos;o&apos;.
String scharenew String(a,1,4);
a[1]= &apos;s &apos; :
sChar变量的值是字符串"ellio"。该构造方法使用字符数组中的部分连接元素来创建字符串对象。offset参数指定起始末引角, count 指定载现元素的个数,创建字符串对象后,即使在后面修改了a数相中第2个元素的值,对sChar的值也没有任何影响。

二、String和int的相互转换
1、String转换为int

String 字符串转整型 int 有以下两种方式:

1、Integer.parseInt(str)

2、Integer.valueOf(str).intValue()

2、int转换为String
整型int转String字符串类型有以下3种方法:
1. Strings=String,valueOf(i);
2. Strings=Integer.toStringGi.
3. Strings=""+"

使用第三种方法相对第一第二种耗时比较大。在使用第一种walueOf()方法时,注意valueOf括号中的值不能为空,否则会报空指针异常(NullPointerException)。
 toString()
toString()可以把一个引用类型转换为Suring字符串类型,是sun公司开发Java的时候为了方便所有类的字符串操作而特意加入的一个方法。
这里chars是存放字符的数组, startindex是字符数组中期望得到的子字符串的首字符下标,numChars指定子字符串的长度。2) parse()
parseXxx(String)这种形式,是指把字符串转换为数值型,其中Xxx对应不同的数据类型,然后转换为Xxx指定的类型,如int型和float型。
3) toString()
toString()可以把一个引用类型转换为Suring字符串类型,是sun公司开发Java的时候为了方便所有类的字符串操作而特意加入的一个方法。
三、字符串拼接(连接)
对于已经定义的字符串,可以对其进行各种操作。连接多个字符串是字符串操作中最简单的一种。通过字符串连接,可以将两个或多个字符串、字符、整数和浮点数等类型的数据连成一个更大的字符串。
String字符串虽然是不可变字符串,但也可以进行拼接只是会产生一个新的对象。String字符串拼接可以使用“+”运算符或String的concat(string str)方法。"+"运算符优势是可以连接任何类型数据拼接成为字符串,而concat方法只能拼接String类型字符串。
1、使用连接运算符“+”
与绝大多数的程序设计语言一样, Java语言允许使用“+”号连接(拼接)两个字符串。“+”远算符是最简单、最快捷,也是使用最多的字符串连接方式。在使用“+”运算符连接字符串和int型(或double型)数据时,“+”将int(或double)型数据自动转换成String类型。

2、使用 concat() 方法

在 Java 中,String 类的 concat() 方法实现了将一个字符串连接到另一个字符串的后面

格式:字符串 1.concat(字符串 2);

3、连接其他类型数据

        前面介绍的例子都是字符串与字符串进行连接,其实字符串也可同其他基本数据类型进行连接。如果将字符串同这些数据类型数据进行连接,此时会将这些数据直接转换成字符串。

四、获取字符串长度(length())

1、要获取字符串的长度,可以使用 String 类的 length() 方法,其语法形式如下:

字符串名.length();

五、字符串大小写转换

String 类的 toLowerCase() 方法可以将字符串中的所有字符全部转换成小写,而非字母的字符不受影响。语法格式如下:

字符串名.toLowerCase()    // 将字符串中的字母全部转换为小写,非字母不受影响

toUpperCase() 则将字符串中的所有字符全部转换成大写,而非字母的字符不受影响。语法格式如下:

```java

字符串名.toUpperCase()    // 将字符串中的字母全部转换为大写,非字母不受影响

```

六、去除字符串中的空格(trim())

字符串中存在的首尾空格一般情况下都没有任何意义,如字符串“ Hello ”,但是这些空格会影响到字符串的操作,如连接字符串或比较字符串等,所以应该去掉字符串中的首尾空格,这需要使用 String 类提供的 trim() 方法。

trim() 方法的语法形式如下:

```java

字符串名.trim()

使用 trim() 方法的示例如下:

String str = " hello ";

System.out.println(str.length());    // 输出 7

System.out.println(str.trim().length());    // 输出 5

    如果不确定要操作的字符串首尾是否有空格,最好在操作之前调用该字符串的 trim() 方法去除首尾空格,然后再对其进行操作。

trim() 只能去掉字符串中前后的半角空格(英文空格),而无法去掉全角空格(中文空格)。可用以下代码将全角空格替换为半角空格再进行操作,其中替换是 String 类的 replace() 方法。

```java

str = str.replace((char) 12288, ' ');    // 将中文空格替换为英文空格

str = str.trim();

其中,12288 是中文全角空格的 unicode 编码

七、截取(提取)子字符串(substring())

        在 String 中提供了两个截取字符串的方法,一个是从指定位置截取到字符串结尾,另一个是截取指定范围的内容。下面对这两种方法分别进行介绍。

1、substring(int beginIndex) 形式

        此方式用于提取从索引位置开始至结尾处的字符串部分。调用时,括号中是需要提取字符串的开始位置,方法的返回值是提取的字符串。例如:

String str = "Hello Java";

String result = str.substring(3);

System.out.println(result);    // 输出:lo Java

2、substring(int begin,int end) 形式

        此方法中的 begin 表示截取的起始索引,截取的字符串中包括起始索引对应的字符;end 表示结束索引,截取的字符串中不包括结束索引对应的字符,如果不指定 end,则表示截取到目标字符串末尾。该方法用于提取位置 begin 和位置 end 位置之间的字符串部分。

        这里需要特别注意的是, 对于开始位置 begin, Java 是基于字符串的首字符索引为 0 处理的,但是对于结束位置 end,Java 是基于字符串的首字符索引为 1 来处理的,如下图所示。

        注意:substring() 方法是按字符截取,而不是按字节截取

课堂实例:

public class Demo {

    public static void main(String[] args) {

        /**

         * 字符串截取

         * substring(int start): 从指定位置开始截取,start表示开始位置下标

         * substring(int start, int end):截取指定范围内的字符串,含前不含尾

         */

        String number = "522226199999999998";

        String substring = number.substring(14);

        System.out.println(substring);

        System.out.println(number.substring(6, 14));

    }

}

public class Demo01 {

    public static void main(String[] args) {

        /**

         * 将字符数组转为字符串

         */

        char[] ch = new char[]{'H', 'e', 'l', 'l', 'o'};

        // 创建建字符串

        String str = new String(ch);

        System.out.println(str);

        /**

         * 分配一个新字符串,该字符串包含字符数组参数的子数组中的字符。

         * offset参数是子数组第一个字符的索引,count参数指定子数组的长度。

         * 复制子数组的内容;字符数组的后续修改不会影响新创建的字符串。

         */

        char[] ch1 = new char[]{'H', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd'};

        String str1 = new String(ch1, 2, 8);

        System.out.println(str1);

        /**

         * 分配一个新字符串,该字符串包含来自Unicode码点数组参数子数组的字符。

         * offset参数是子数组第一个编码点的索引,count参数指定子数组的长度。

         * 子数组的内容被转换为字符;int数组的后续修改不会影响新创建的字符串。

         */

        int[] arr = new int[]{65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75};

        String str2 = new String(arr, 0, 5);

        System.out.println(str2);

    }

}

public class Demo02 {

    public static void main(String[] args) {

        /**

         * 将字符串转为int类型数据

         *

         * 注意:

         *      将字符串转为int类型的值前提是字符内容必须是纯数字

         */

        // 使用包装类进行转换

        String str1 = "56";

        int num1 = Integer.parseInt(str1);

        System.out.println(num1 + 1);

        int num2 = Integer.valueOf(str1).intValue();

        System.out.println(num2 + 1);

        String str2 = "Hello56";

        String str3 = "12World"; //

        // int num3 = Integer.parseInt(str2); // java.lang.NumberFormatException: For input string: "Hello56"

        int num4 = Integer.parseInt(str3); // java.lang.NumberFormatException: For input string: "12World"

    }

}

public class Demo03 {

    public static void main(String[] args) {

        /**

         * 将int类型数据转为String类型

         */

        // 第一种方式

        int num1 = 56;

        String str = num1 + "";

        System.out.println(str + 1);// 561

        // String s = String.valueOf(i);

        String str2 = String.valueOf(89);

        System.out.println(str2 + 1); // 891

        // String s = Integer.toString(i);

        String str3 = Integer.toString(90);

        System.out.println(str3 + 1); //901

    }

}

public class Demo04 {

    public static void main(String[] args) {

        String str = "Hello";

        String s1 = "world";

        /**

         * 字符串+运算

         */

        // 获取系统时间戳

        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000; i++) {

            str += s1;

        }

        long end = System.currentTimeMillis();

        System.out.println(end - start);

        // 使用concat方法进行字符串拼接

        String s2 = "张三";

        String s3 = "李四";

        String s4 = s2.concat(s3);

        System.out.println(s4);

    }

}

public class Demo05 {

    public static void main(String[] args) {

        /**

         * 1、获取字符串长度

         * 2、将字符串转大小写

         * 3、去除字符串两端的空白

         */

        String str = "Hello Java";

        // 使用length()函数获取字符串长度

        System.out.println(str.length());

        // 将字符串转为大写

        System.out.println(str.toUpperCase());

        // 将字符串转小写

        System.out.println(str.toLowerCase());

        String str1 = "              hello                     ";

        System.out.println(str1.length());

        // 取出字符串两端空白

        String str2 = str1.trim();

        System.out.println(str2.length());

    }

}

public class Demo05Test {

    public static void main(String[] args) {

        /**

         * 要求:

         *      用户从控制台输入应该长度为4的验证码,验证码的内容为字符或者数字

         *      后台接收到用户输入内容之后统一将其转为大写和小写

         */

        Scanner scan = new Scanner(System.in);

        System.out.println("请输入长度为4的验证码: ");

        // 接收用户输入的值

        String val = scan.nextLine();

        // 取出val两端的空白

        val = val.trim();

        // 验证长度

        if (val.length() == 4) {

            // 长度为4,则进行转大小写

            System.out.println(val.toUpperCase());

            System.out.println(val.toLowerCase());

        }else{

            System.out.println("您输入的长度有误");

        }

    }

}

补、字符串的分割和截取

八、分割字符串(spilt())

tring 类的 split() 方法可以按指定的分割符对目标字符串进行分割,分割后的内容存放在字符串数组中。该方法主要有如下两种重载形式:

```java

str.split(String sign)

str.split(String sign,int limit)

其中它们的含义如下:

**str:**为需要分割的目标字符串。

**sign:**为指定的分割符,可以是任意字符串。

**limit:**表示分割后生成的字符串的限制个数,如果不指定,则表示不限制,直到将整个目标字符串完全分割为止。

使用分隔符注意如下:

1)“.”和“|”都是转义字符,必须得加“\\”。

如果用“.”作为分隔的话,必须写成`String.split("\\.")`,这样才能正确的分隔开,不能用`String.split(".")`。

如果用“|”作为分隔的话,必须写成`String.split("\\|")`,这样才能正确的分隔开,不能用`String.split("|")`。

2)如果在一个字符串中有多个分隔符,可以用“|”作为连字符,比如:“acount=? and uu =? or n=?”,把三个都分隔出来,可以       用`String.split("and|or")`。

九、案例:截取新闻标题

        在新闻网站中通常以列表的形式显示最新新闻的动态标题。一般情况下,一行显示一条新闻标题,而新闻标题往往比较长,因此需要对它进行截取,将超出部分显示成一个省略号“…”。

public class Demo07 {

    public static void main(String[] args) {

        // 定义存储新闻标题的数组

        String[] news = new String[] { "如何快速掌握Java", "听老王剖析Java中的运算符",

                "学习Java的十大忠告", "你所不知道的java网络编程技巧大全", "Java面试题大全" };

        for (int i = 0; i < news.length; i++) {

            if(news[i].length() > 10) {

                // 截取新闻标题

                System.out.println(news[i].substring(0, 10) + "....");

            }else{

                // 长度不够,不截取

                System.out.println(news[i]);

            }

        }

    }

}

在该程序中,首先定义了存储新闻标题的数组,元素类型为 String 类型,然后循环遍历该数组,在循环体中,判断数组中的元素长度是否大于 10,如果大于,则使用 String 类的 substring() 方法截取前 10 个字符并输出,否则将数组中的元素输出即可。

十、字符串的替换

        因为要截取的是新闻标题中的前 10 个字符,因此,起始位置从 0 开始,结束位置为 10(索引从 1 开始),即使用 substring(0,10) 就完成了新闻标题的截取操作。

1、replace() 方法

replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串),其语法格式如下:

字符串.replace(String oldChar, String newChar)

其中,oldChar 表示被替换的字符串;newChar 表示用于替换的字符串。replace() 方法会将字符串中所有 oldChar 替换成 newChar。

public class Demo08 {

    public static void main(String[] args) {

        /**

         * 字符串替换

         *      replace("o", "v")

         */

        String str = "Hello Jaoa";

        String replace = str.replace("o", "v");

        System.out.println(replace);

        /**

         * 替换敏感字符

         */

        String s = "你个DSB,TMD会不会玩,你就是应该DJB,去NMD";

        s = s.replace("DSB", "*");

        s = s.replace("TMD", "*");

        s = s.replace("DJB", "*");

        s = s.replace("NMD", "*");

        System.out.println(s);

    }

}

1replace() 方法

        replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串),其语法格式如下:

字符串.replace(String oldChar, String newChar)

        其中,oldChar 表示被替换的字符串;newChar 表示用于替换的字符串。replace() 方法会将字符串中所有 oldChar 替换成 newChar。

1

       创建一个字符串,对它使用 replace() 方法进行字符串替换并输出结果。代码如下:

public static void main(String[] args) {

   String words = "hello java,hello php";

   

   System.out.println("原始字符串是'"+words+"'");

   System.out.println("replace(\"l\",\"D\")结果:"+words.replace("l","D"));

   System.out.println("replace(\"hello\",\"你好\")结果:"+words.replace("hello","你好 "));

   

   words = "hr's dog";

   

   System.out.println("原始字符串是'"+words+"'");

   System.out.println("replace(\"r's\",\"is\")结果:"+words.replace("r's","is"));

}

       输出结果如下所示:

原始字符串是'hello java,hello php'

replace("l","D")结果:heDDo java,heDDo php

replace("hello","你好")结果:你好 java,你好 php

原始字符串是'hr's dog'

replace("r's","is")结果:his dog

2replaceFirst() 方法

       replaceFirst() 方法用于将目标字符串中匹配某正则表达式的第一个子字符串替换成新的字符串,其语法形式如下:

字符串.replaceFirst(String regex, String replacement)

       其中,regex 表示正则表达式;replacement 表示用于替换的字符串。例如:

String words = "hello java,hello php";

String newStr = words.replaceFirst("hello","你好 ");

System.out.println(newStr);    // 输出:你好 java,hello php

3replaceAll() 方法

       replaceAll() 方法用于将目标字符串中匹配某正则表达式的所有子字符串替换成新的字符串,其语法形式如下:

字符串.replaceAll(String regex, String replacement)

       其中,regex 表示正则表达式,replacement 表示用于替换的字符串。例如:

String words = "hello java,hello php";

String newStr = words.replaceAll("hello","你好 ");

System.out.println(newStr);    // 输出:你好 java,你好 php

十一、案例:替换敏感字符

假设有一段文本里面有很多敏感词汇。现在使用 Java 中的字符串替换方法对它进行批量修改和纠正,就可以用到 String 类的 replace() 方法、replaceFirst() 方法和 replaceAll() 方法。

public class Demo08 {

    public static void main(String[] args) {

        /**

         * 字符串替换

         *      replace("o", "v")

         */

        String str = "Hello Jaoa";

        String replace = str.replace("o", "v");

        System.out.println(replace);

        /**

         * 替换敏感字符

         */

        String s = "你个DSB,TMD会不会玩,你就是应该DJB,去NMD";

        s = s.replace("DSB", "*");

        s = s.replace("TMD", "*");

        s = s.replace("DJB", "*");

        s = s.replace("NMD", "*");

        System.out.println(s);

    }

}

# 十二、字符串比较

​        字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀串等。在 Java 中,比较字符串的常用方法有 3 个:equals() 方法、equalsIgnoreCase() 方法、 compareTo() 方法。

## 1、equals() 方法

​        equals() 方法将逐个地比较两个字符串的每个字符是否相同。如果两个字符串具有相同的字符和长度,它返回 true,否则返回 false。对于字符的大小写,也在检查的范围之内。equals() 方法的语法格式如下:

```java

str1.equals(str2);

​        str1 和 str2 可以是字符串变量, 也可以是字符串字面量。 例如, 下列表达式是合法的:

```java

"Hello".equals(greeting)

下面的代码说明了 equals() 方法的使用:

java

String str1 = "abc";

String str2 = new String("abc");

String str3 = "ABC";

System.out.println(str1.equals(str2)); // 输出 true

System.out.println(str1.equals(str3)); // 输出 false

```

public class Demo09 {

    public static void main(String[] args) {

        /**

         * 字符串的比较

         *      1、==

         *      2、equals()

         *      3、equalsIgnoreCase(s2)

         */

        String s1 = "java";

        String s2 = "JAVA";

        System.out.println(s1 == s2);

        System.out.println(s1.equals(s2));

        System.out.println(s1.equalsIgnoreCase(s2));

    }

}

2、equalsIgnoreCase() 方法

equalsIgnoreCase() 方法的作用和语法与 equals() 方法完全相同,唯一不同的是 equalsIgnoreCase() 比较时不区分大小写。当比较两个字符串时,它会认为 A-Z 和 a-z 是一样的。

下面的代码说明了 equalsIgnoreCase() 的使用:

java

String str1 = "abc";

String str2 = "ABC";

System.out.println(str1.equalsIgnoreCase(str2));    // 输出 true

## 3、equals()与==的比较

理解 equals() 方法和`==`运算符执行的是两个不同的操作是重要的。如同刚才解释的那样,equals() 方法比较字符串对象中的字符。而`==`运算符比较两个对象引用看它们是否引用相同的实例。

        下面的程序说明了两个不同的字符串(String)对象是如何能够包含相同字符的,但同时这些对象引用是不相等的:

```java

String s1 = "Hello";

String s2 = new String(s1);

System.out.println(s1.equals(s2)); // 输出true

System.out.println(s1 == s2); // 输出false

变量 s1 指向由“Hello”创建的字符串实例。s2 所指的的对象是以 s1 作为初始化而创建的。因此这两个字符串对象的内容是一样的。但它们是不同的对象,这就意味着 s1 和 s2 没有指向同一的对象,因此它们是不`==`的。

因此,千万不要使用`==`运算符测试字符串的相等性,以免在程序中出现糟糕的 bug。从表面上看,这种 bug 很像随机产生的间歇性错误。

# 十三、字符串查找

在给定的字符串中查找字符或字符串是比较常见的操作。字符串查找分为两种形式:一种是在字符串中获取匹配字符(串)的索引值,另一种是在字符串中获取指定索引位置的字符。

## 1、根据字符查找

String 类的 indexOf() 方法和 lastlndexOf() 方法用于在字符串中获取匹配字符(串)的索引值。

### 1.1、indexOf() 方法

indexOf() 方法用于返回字符(串)在指定字符串中首次出现的索引位置,如果能找到,则返回索引值,否则返回 -1。该方法主要有两种重载形式:

`java

str.indexOf(value)

str.indexOf(value,int fromIndex)

其中,str 表示指定字符串;value 表示待查找的字符(串);fromIndex 表示查找时的起始索引,如果不指定 fromIndex,则默认从指定字符串中的开始位置(即 fromIndex 默认为 0)开始查找。

例如,下列代码在字符串“Hello Java”中查找字母 v 的索引位置。

`java

String s = "Hello Java";int size = s.indexOf('v');    // size的结果为8

上述代码执行后 size 的结果为 8,

### 1.2、lastlndexOf() 方法

lastIndexOf() 方法用于返回字符(串)在指定字符串中最后一次出现的索引位置,如果能找到则返回索引值,否则返回 -1。该方法也有两种重载形式:

java

str.lastIndexOf(value)str.lastlndexOf(value, int fromIndex)

**注意:**lastIndexOf() 方法的查找策略是从右往左查找,如果不指定起始索引,则默认从字符串的末尾开始查找。

## 2、根据索引查找

String 类的 charAt() 方法可以在字符串内根据指定的索引查找字符,该方法的语法形式如下:

`java

**提示:**字符串本质上是字符数组,因此它也有索引,索引从零开始。

public class Demo10 {

    public static void main(String[] args) {

        /**

         * indexOf() 方法用于返回字符(串)在指定字符串中首次出现的索引位置,

         * 如果能找到,则返回索引值,否则返回 -1

         */

        String str = "indexOf() 方法用于返回字符(串)在指定字符串中首次出现的索引位置,如果能找到,则返回索引值,否则返回 -1";

        // 查找 ”字符“

        System.out.println(str.indexOf("字符"));

        System.out.println(str.indexOf("大锤"));

        // 从指定位置开始查找

        System.out.println(str.indexOf('字', 17));

        /**

         * lastIndexOf() 方法用于返回字符(串)在指定字符串中最后一次出现的索引位置,

         * 如果能找到则返回索引值,否则返回 -1。

         */

        String  s = "lastIndexOf() 方法用于返回字符(串)在指定字符串中最后一次出现的索引位置,如果能找到则返回索引值,否则返回 -1。";

        System.out.println(s.lastIndexOf("索引值"));

        /**

         * 案例:用户给定文件名字(完整名字),程序判断文件类型

         */

        Scanner scan = new Scanner(System.in);

        System.out.println("请输入完整的文件名字(包括文件后缀): ");

        String fileName = scan.nextLine();

        // 处理用户输入的内容

        fileName = fileName.toLowerCase();

        fileName = fileName.trim();

        // 获取文件的后缀 a.txt  b.a.exe

        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);

        System.out.println(suffix);

        // 判断文件的类型

        switch (suffix) {

            case "txt":

                System.out.println("文本文件");

                break;

            case "exe":

                System.out.println("win可执行程序");

                break;

            case "word":

                System.out.println("word文档");

                break;

            case "html":

                System.out.println("网页的文件");

                break;

            default:

                System.out.println("未知的文件");

                break;

        }

    }

}

# 十四、StringBuffer 类

在 Java 中,除了通过 String 类创建和处理字符串之外,还可以使用 StringBuffer 类来处理字符串。StringBuffer 类可以比 String 类更高效地处理字符串。

因为 StringBuffer 类是可变字符串类,创建 StringBuffer 类的对象后可以随意修改字符串的内容。每个 StringBuffer 类的对象都能够存储指定容量的字符串,如果字符串的长度超过了 StringBuffer 类对象的容量,则该对象的容量会自动扩大。

## 1、创建 StringBuffer 类

StringBuffer 类提供了 3 个构造方法来创建一个字符串,如下所示:

1、StringBuffer() 构造一个空的字符串缓冲区,并且初始化为 16 个字符的容量。

2、StringBuffer(int length) 创建一个空的字符串缓冲区,并且初始化为指定长度 length 的容量。

3、StringBuffer(String str) 创建一个字符串缓冲区,并将其内容初始化为指定的字符串内容 str,字符串缓冲区的初始容量为      16 加上字符串 str 的长度。

## 2、追加字符串

StringBuffer 类的 append() 方法用于向原有 StringBuffer 对象中追加字符串。该方法的语法格式如下:

java

StringBuffer 对象.append(String str)

该方法的作用是追加内容到当前 StringBuffer 对象的末尾,类似于字符串的连接

## 3、替换字符

StringBuffer 类的 setCharAt() 方法用于在字符串的指定索引位置替换一个字符。该方法的语法格式如下:

java

StringBuffer 对象.setCharAt(int index, char ch);

该方法的作用是修改对象中索引值为 index 位置的字符为新的字符 ch

public class Demo11 {

    public static void main(String[] args) {

        String str = "hello java";

        // 查找字符

        System.out.println(str.charAt(3));

    }

}

4、反转字符串

       StringBuffer 类中的 reverse() 方法用于将字符串序列用其反转的形式取代。该方法的语法格式如下:

StringBuffer 对象.reverse();

       使用 StringBuffer 类中的 reverse() 方法对字符串进行反转的示例如下:

StringBuffer sb = new StringBuffer("java");

sb.reverse();

System.out.println(sb);    // 输出:avaj

5、删除字符串

StringBuffer 类提供了 deleteCharAt() 和 delete() 两个删除字符串的方法,下面详细介绍。

1、deleteCharAt() 方法

deleteCharAt() 方法用于移除序列中指定位置的字符,该方法的语法格式如下:

StringBuffer 对象.deleteCharAt(int index);

deleteCharAt() 方法的作用是删除指定位置的字符,然后将剩余的内容形成一个新的字符串。例如:

StringBuffer sb = new StringBuffer("She");

sb.deleteCharAt(2);

System.out.println(sb);    // 输出:Sh

执行该段代码,将字符串 sb 中索引值为 2 的字符删除,剩余的内容组成一个新的字符串,因此对象 sb 的值为 Sh。

2、delete() 方法

delete() 方法用于移除序列中子字符串的字符,该方法的语法格式如下:

StringBuffer 对象.delete(int start,int end);

其中,start 表示要删除字符的起始索引值(包括索引值所对应的字符),end 表示要删除字符串的结束索引值(不包括索引值所对应的字符)。该方法的作用是删除指定区域以内的所有字符,例如:

StringBuffer sb = new StringBuffer("hello jack");

sb.delete(2,5);

System.out.println(sb);    // 输出:he jack

sb.delete(2,5);

System.out.println(sb);    // 输出:heck

执行该段代码,将字符串“hello jack”索引值为 2(包括)到索引值为 5(不包括)之间的所有字符删除,因此输出的新的字符串的值为“he jack”。

# 十五、String、StringBuffer和StringBuilder区别

在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。String 类是不可变类,即一旦一个 String 对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。

Java 提供了两个可变字符串类 StringBuffer 和 StringBuilder,中文翻译为“字符串缓冲区”。

StringBuilder 类是 JDK 1.5 新增的类,它也代表可变字符串对象。实际上,StringBuilder 和 StringBuffer 功能基本相似,方法也差不多。不同的是,StringBuffer 是线程安全的,而 StringBuilder 则没有实现线程安全功能,所以性能略高。因此在通常情况下,如果需要创建一个内容可变的字符串对象,则应该优先考虑使用 StringBuilder 类。

StringBuffer、StringBuilder、String 中都实现了 CharSequence 接口。CharSequence 是一个定义字符串操作的接口,它只包括 length()、charAt(int index)、subSequence(int start, int end) 这几个 API。

StringBuffer、StringBuilder、String 对 CharSequence 接口的实现过程不一样,如下图 1 所示:

<img src="01-String 字符串处理.assets\image-20220929165136354.png" alt="image-20220929165136354" style="zoom:80%;" />

可见,String 直接实现了 CharSequence 接口,StringBuilder 和 StringBuffer 都是可变的字符序列,它们都继承于 AbstractStringBuilder,实现了 CharSequence 接口。

public class Demo01 {

    public static void main(String[] args) {

        // 创建StringBuffer对象

        StringBuffer sb = new StringBuffer();

        System.out.println(sb.length());

        StringBuffer sb2 = new StringBuffer(32);

        System.out.println(sb2.length());

        StringBuffer sb3 = new StringBuffer("Hello Java");

        System.out.println(sb3.length());

        /*

         *输出字符串的容量大小

         *capacity()方法返回字符串的容量大小

         */

        System.out.println(sb.capacity());

        System.out.println(sb2.capacity());

        System.out.println(sb3.capacity());

    }

}

public class Demo02 {

    public static void main(String[] args) {

        String str = "java";

        StringBuffer sb = new StringBuffer("java");

        // String类的拼接

        long s1 = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++) {

            str = str + "Hello";

        }

        long end1 = System.currentTimeMillis();

        System.out.println(end1 - s1);

        // append方法拼接字符串

        long s2 = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++) {

            sb.append("Hello");

        }

        long end2 = System.currentTimeMillis();

        System.out.println(end2 - s2);

        System.out.println(str.equalsIgnoreCase(sb.toString()));

    }

}

public class Demo03 {

    public static void main(String[] args) {

        StringBuffer sb = new StringBuffer("Hello java");

        // 替换字符setCharAt(int index, char ch)

        sb.setCharAt(4, 'O');

        System.out.println(sb.toString());

    }

}

public class Demo04 {

    public static void main(String[] args) {

        /**

         * 字符串反转

         */

        String str = "Hello java";

        StringBuffer sb = new StringBuffer("Hello Java");

        StringBuffer reverse = sb.reverse();

        System.out.println(reverse.toString());

        // 判断回文

        // 上海自来水来自海上

        StringBuffer sb1 = new StringBuffer("上海自来水来自海上");

        // 将sb1反转

        StringBuffer sb2 = sb1.reverse();

        System.out.println(sb1.equals(sb2));

        System.out.println(sb1.toString());

        /**

         * deleteCharAt() 方法用于移除序列中指定位置的字符

         */

        sb1.deleteCharAt(1);

        System.out.println(sb1.toString());

        /**

         * delete() 方法用于移除序列中子字符串的字符

         */

        sb1.delete(1, 4);

        System.out.println(sb1.toString());

        System.out.println("******************************************");

        /**

         * insert()方法插入字符串

         */

        StringBuffer sb3 = new StringBuffer();

        sb3.append("Hello");

        System.out.println(sb3);

        sb3.insert(1, new char[]{'5','A'});

        System.out.println(sb3);

    }

}

String Bulider

public class Demo01 {

    public static void main(String[] args) {

        /**

         * StringBuilder

         */

        StringBuffer sf = new StringBuffer("java");

        StringBuilder sl = new StringBuilder("java");

        long sf1 = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++) {

            sf.append("Python");

        }

        long sf2 = System.currentTimeMillis();

        System.out.println(sf2-sf1);

        long sl1 = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++) {

            sl.append("Python");

        }

        long sl2 = System.currentTimeMillis();

        System.out.println(sl2-sl1);

    }

}

02数字和日期处理

一、Math类的常用方法

Java 中的 +、-、*、/ 和 % 等基本算术运算符不能进行更复杂的数学运算,例如,三角函数、对数运算、指数运算等。于是 Java 提供了 Math 工具类来完成这些复杂的运算。

在 Java 中 Math 类封装了常用的数学运算,提供了基本的数学操作,如指数、对数、平方根和三角函数等。Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 Math 类的对象,并且 Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。

1、静态常量

Math 类中包含 E 和 PI 两个静态常量,正如它们名字所暗示的,它们的值分别等于 e(自然对数)和 π(圆周率)。

例 1

调用 Math 类的 E 和 PI 两个常量,并将结果输出。代码如下:

System.out.println("E 常量的值:" + Math.E);

System.out.println("PI 常量的值:" + Math.PI);

2、求最大值、最小值和绝对值

在程序中常见的就是求最大值、最小值和绝对值问题,如果使用 Math 类提供的方法可以很容易实现。这些方法的说明如下表所示。

方法

说明

static int abs(int a)

返回 a 的绝对值

static long abs(long a)

返回 a 的绝对值

static float abs(float a)

返回 a 的绝对值

static double abs(double a)

返回 a 的绝对值

static int max(int x,int y)

返回 x 和 y 中的最大值

static double max(double x,double y)

返回 x 和 y 中的最大值

static long max(long x,long y)

返回 x 和 y 中的最大值

static float max(float x,float y)

返回 x 和 y 中的最大值

static int min(int x,int y)

返回 x 和 y 中的最小值

static long min(long x,long y)

返回 x 和 y 中的最小值

static double min(double x,double y)

返回 x 和 y 中的最小值

static float min(float x,float y)

返回 x 和 y 中的最小值

## 3、求整运算

Math 类的求整方法有很多,详细说明如下表所示。

| 方法                          | 说明                                                         |

| ----------------------------- | ------------------------------------------------------------ |

| static double ceil(double a)  | 返回大于或等于 a 的最小整数                                 

| static double floor(double a) | 返回小于或等于 a 的最大整数                                 

| static double rint(double a)  | 返回最接近 a 的整数值,如果有两个同样接近的整数,则结果取偶数 |

| static int round(float a)     | 将参数加上 1/2 后返回与参数最近的整数                       

| static long round(double a)   | 将参数加上 1/2 后返回与参数最近的整数,然后强制转换为长整型  |

## 5、指数运算

指数的运算包括求方根、取对数及其求 n 次方的运算。在 Math 类中定义的指数运算方法及其说明如下表所示。

| 方法                                 | 说明                               |

| ------------------------------------ | ---------------------------------- |

| static double exp(double a)          | 返回 e 的 a 次幂                   |

| static double pow(double a,double b) | 返回以 a 为底数,以 b 为指数的幂值 |

| static double sqrt(double a)         | 返回 a 的平方根                    |

| static double cbrt(double a)         | 返回 a 的立方根                    |

| static double log(double a)          | 返回 a 的自然对数,即 lna 的值     |

| static double log10(double a)        | 返回以 10 为底 a 的对数            |

二、生成随机数(random()和Random类)

在 Java 中要生成一个指定范围之内的随机数字有两种方法:一种是调用 Math 类的 random() 方法,一种是使用 Random 类。

Random 类提供了丰富的随机数生成方法,可以产生 boolean、int、long、float、byte 数组以及 double 类型的随机数,这是它与 random() 方法最大的不同之处。random() 方法只能产生 double 类型的 0~1 的随机数。

Random 类位于 java.util 包中,该类常用的有如下两个构造方法。

**Random():**该构造方法使用一个和当前系统时间对应的数字作为种子数,然后使用这个种子数构造 Random 对象。

**Random(long seed):**使用单个 long 类型的参数创建一个新的随机数生成器。

Random 类提供的所有方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的概率是均等的,在表 1 中列出了 Random 类中常用的方法。

| 方法                    | 说明                                                         |

| ----------------------- | ------------------------------------------------------------ |

| boolean nextBoolean()   | 生成一个随机的 boolean 值,生成 true 和 false 的值概率相等   |

| double nextDouble()     | 生成一个随机的 double 值,数值介于 [0,1.0),含 0 而不包含 1.0 |

| int nextlnt()           | 生成一个随机的 int 值,该值介于 int 的区间,也就是 -231~231-1。如果 需要生成指定区间的 int 值,则需要进行一定的数学变换 |

| int nextlnt(int n)      | 生成一个随机的 int 值,该值介于 [0,n),包含 0 而不包含 n。如果想生成 指定区间的 int 值,也需要进行一定的数学变换 |

| void setSeed(long seed) | 重新设置 Random 对象中的种子数。设置完种子数以后的 Random 对象 和相同种子数使用 new 关键字创建出的 Random 对象相同 |

| long nextLong()         | 返回一个随机长整型数字                                       |

| boolean nextBoolean()   | 返回一个随机布尔型值                                         |

| float nextFloat()       | 返回一个随机浮点型数字                                       |

| double nextDouble()     | 返回一个随机双精度值                                         |

课堂实例:

public class Demo02 {

    public static void main(String[] args) {

        /**

         * Random 类提供了丰富的随机数生成方法,可以产生 boolean、int、long、float、byte

         * 数组以及 double 类型的随机数,这是它与 Math.random() 方法最大的不同之处。random()

         * 方法只能产生 double 类型的 0~1 的随机数。

         */

        // 第一种生成随机数的方法,Math.random()

        for (int i = 0; i < 10; i++) {

            // System.out.println(Math.random());

            // 生成10以内的随机数

            // System.out.println((int) (Math.random() * 10));

            // 生成10-20之间的随机数

            // System.out.println(10 + (int) (Math.random() * 10));

            // 生成50以内的随机数

            System.out.println((int) (Math.random() * 50));

        }

        System.out.println("*****************************************");

        // 第二种方式生成随机数,Random rand = new Random(); rand.nextXXX()

        // 创建随机数Random对象

        Random rand = new Random();

        for (int i = 0; i < 10; i++) {

            // System.out.println(rand.nextInt(10));

            System.out.println(rand.nextDouble());

        }

    }

}

数字格式化

代码:

package com.zpark.math;

import java.text.DecimalFormat;

public class Demo03 {

    public static void main(String[] args) {

        /**

         * 数字格式化

         */

        // 实例化数字格式化对象,并且指定格式

        DecimalFormat df1 = new DecimalFormat("0.0");

        DecimalFormat df2 = new DecimalFormat("#.#");

        DecimalFormat df3 = new DecimalFormat("000.000");

        DecimalFormat df4 = new DecimalFormat("###.###");

        // 格式化 3789.14159

        double d1 = 3789.14159;

        System.out.println(df1.format(d1));

        System.out.println(df2.format(d1));

        System.out.println(df3.format(d1));

        System.out.println(df4.format(d1));

        // 格式化1999

        int d2 = 1999;

        System.out.println(df1.format(d2));

        System.out.println(df2.format(d2));

        System.out.println(df3.format(d2));

        System.out.println(df4.format(d2));

    }

}

java大数字运算
 
    在 Java 中提供了用于大数字运算的类,即 java.math.BigInteger 类和 java.math.BigDecimal 类。这两个类用于高精度计算,其中 BigInteger 类是针对整型大数字的处理类,而 BigDecimal 类是针对大小数的处理类
代码:
package com.zpark.math;
 
import java.math.BigDecimal;
 
public class Demo04 {
    public static void main(String[] args) {
        /**
         * java大数字运算
         */
        BigDecimal bd = new BigDecimal(3.14159263578461259445);
        bd = bd.add(new BigDecimal(3.14159263578461259445));
        System.out.println(bd);
 
    }
}
 
 

Java时间日期的处理

Date 类

Date 类表示系统特定的时间戳,可以精确到毫秒。Date 对象表示时间的默认顺序是星期、月、日、小时、分、秒、年。

代码:

package com.zpark.data;

import java.util.Date;

public class Demo01 {

    public static void main(String[] args) {

        /**

         * date类简介

         */

        // 获取系统当前时间

        Date date = new Date();

        System.out.println(date);

        // 创建date对象,并且指定时间

        Date d1 = new Date(60000);

        System.out.println(d1);

        //判断此日期是否在指定日期之后

        boolean after = date.after(d1);

        System.out.println(after);

        // 判断此日期是否在指定日期之前

        boolean before = date.before(d1);

        System.out.println(before);

        // 比较两个日期的顺序 返回1表示date在d1之后,0表示相等(同一时刻), -1表示之前

        int i = date.compareTo(d1);

        System.out.println(i);

        // 获取毫秒值

        System.out.println(date.getTime());

        System.out.println(System.currentTimeMillis());

        System.out.println(date.getHours());

    }

}

Calendar 
 
代码:
package com.zpark.date;
 
import java.util.Calendar;
 
public class Demo02 {
    public static void main(String[] args) {
        /**
         * Calendar 类
         */
        // Calendar 类对象
        Calendar cl = Calendar.getInstance();
        // 获取一星期的第一天。根据不同的国家地区,返回不同的值
        System.out.println(cl.getFirstDayOfWeek());
 
        int year = cl.get(Calendar.YEAR); // 获取当前年份
        System.out.println("现在是" + year + "年");
 
 
    }
}
 
日期格式化
 
代码:
package com.zpark.date;
 
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
 
public class Demo03 {
    public static void main(String[] args) throws ParseException {
        /**
         * 日期格式化
         */
        DateFormat df = DateFormat.getDateInstance();
        // 将 Date 格式化日期/时间字符串
        System.out.println(df.format(new Date()));
 
        DateFormat sf1 = DateFormat.getDateTimeInstance();
        System.out.println(sf1.format(new Date()));
 
        String time = "2022-10-19 16:08:28";
        Date d1 = DateFormat.getTimeInstance().parse(time);
        System.out.println(d1);
 
    }
}
 

DateFormat 类

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期→文本)、解析(文本→日期)和标准化日期。

代码:

package com.zpark.date;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Locale;

public class Demo04 {

    public static void main(String[] args) {

        /**

         * 如果使用 DateFormat 类格式化日期/时间并不能满足要求,

         * 那么就需要使用 DateFormat 类的子类——SimpleDateFormat。

         */

        Date date = new Date();

        // 格式化日期

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 这是今年第D天 HH时mm分ss秒SSS毫秒 今天是星期E",

                Locale.CHINA);

        // 格式化日期

        String format = sdf.format(date);

        System.out.println(format);

        // 格式化日期

        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

        System.out.println(sdf1.format(new Date()));

    }

}

三、内置类及包装类

包装类、装箱和拆箱

包装类

Java 的设计中提倡一种思想,即一切皆对象。但是从数据类型的划分中,我们知道 Java 中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?于是 Java 为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有地方称为外覆类或数据类型类。

装箱和拆箱

基本数据类型转换为包装类的过程称为装箱,例如把 int 包装成 Integer 类的对象;包装类变为基本数据类型的过程称为拆箱,例如把 Integer 类的对象重新简化为 int。

代码:

package com.zpark.内置类和包装类;

public class Demo01 {

    public static void main(String[] args) {

        /**

         * 拆箱与装箱:

         *      拆箱:将包装类转为基本数据类型

         *      装箱:将基本数据类型转为包装类

         */

        int num = 1919;

        Integer i1 = num;// 装箱

        int number = i1;// 拆箱

    }

}

Object

Object 是 Java 类库中的一个特殊类,也是所有类的父类。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类

代码:

package com.zpark.内置类和包装类;

import java.io.Serializable;

import java.util.Objects;

public class Demo02 implements Serializable {

    private Integer num = 3;

    public static void main(String[] args) {

        /**

         * object类:

         */

        Demo02 d1 = new Demo02();

        System.out.println(d1.toString());

        Demo02 d2 = new Demo02();

        System.out.println(d2.hashCode() + ": " + d1.hashCode());

        d2.equals(d1);

        String s1 = "Hello";

//        s1.equals()

        Demo02 d3 = new Demo02();

        // 获取字节码对象

        Class<? extends Demo02> aClass = d3.getClass();

        System.out.println(aClass.getName());

        System.out.println(aClass.getPackage());

        // 获取父类的名字

        System.out.println(aClass.getSuperclass().getName());

        // 获取父类的包名

        System.out.println(aClass.getSuperclass().getPackage());

        // 获取实现的接口信息

        Class<?>[] interfaces = aClass.getInterfaces();

        for (int i = 0; i < interfaces.length; i++) {

            System.out.println(interfaces[i]);

        }

        // 接收任意数据类型

        int num = 3;

        Object o1 = num;

        Object o2 = d3;

        Object o3 = "";

    }

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (!(o instanceof Demo02)) return false;

        Demo02 demo02 = (Demo02) o;

        return Objects.equals(num, demo02.num);

    }

    @Override

    public int hashCode() {

        return Objects.hash(num);

    }

    @Override

    public String toString() {

        return "Demo02{" +

                "num=" + num +

                '}';

    }

}

Integer

Integer 类在对象中包装了一个基本类型 int 的值。Integer 类对象包含一个 int 类型的字段。此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。

代码:

package com.zpark.内置类和包装类;

public class Demo03 {

    public static void main(String[] args) {

        /**

         * integer类

          */

        Integer intg1 = new Integer("798");

        // 以 int 类型返回该 Integer 的值

        System.out.println(intg1.intValue() + 1);

        // 返回保存指定的 String 值的 Integer 对象

        Integer value = Integer.valueOf("10000");

        // 获取最大值和最小值

        System.out.println(Integer.MAX_VALUE);

        System.out.println(Integer.MIN_VALUE);

        System.out.println(Integer.SIZE);

        System.out.println(Integer.TYPE);

    }

}

System类

System 类位于 java.lang 包,代表当前 Java 程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类。

代码:

package com.zpark.内置类和包装类;

import java.util.Enumeration;

import java.util.Properties;

public class Demo04 {

    public static void main(String[] args) {

        /**

         * System类

         */

        System.out.println(12);

        // System.in.

        System.err.println(12);

        // 调用垃圾回收器

        System.gc();

        // 获取properties对象

        Properties properties = System.getProperties();

        Enumeration<Object> keys = properties.keys();

        // 循环取数据

        while (keys.hasMoreElements()) {

            // 有下一个元素,取元素

            String key = (String) keys.nextElement();

            String value = properties.getProperty(key);

            System.err.println(key + "------" + value);

        }

        // 退出程序

        System.exit(0);

        System.out.println("程序还未退出");

    }

}

JDK自带记录日志类

代码:

package com.zpark.内置类和包装类;

import java.util.logging.Level;

import java.util.logging.Logger;

public class Demo05 {

    public static void main(String[] args) {

        /**

         * Java自带日志信息类

         */

        Logger.getGlobal().info("打印日志信息");

        int num = 32;

        Logger.getGlobal().info("定义了int类型的num变量,值为:" + num);

        Logger.getGlobal().log(Level.WARNING, "这是警告信息");

        Logger.getGlobal().severe("这是严重的情况");

    }

}

05、异常

一、异常(Exception)处理及常见异常

计算机程序的编写也需要考虑处理这些异常情况。异常(exception)是在运行程序时产生的一种异常情况,已经成为了衡量一门语言是否成熟的标准之一。目前的主流编程语言,如 C++、c#、Ruby 和 Python 等大都提供了异常处理机制。

## 1、异常简介

Java 中的异常又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类,这可以让程序具有极好的容错性且更加健壮。

Java 中一个异常的产生,主要有如下三种原因:

1、Java 内部错误发生异常,Java 虚拟机产生的异常。

2、编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。

3、通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。

Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。

我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常。

2异常类型

2.1为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类。在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 Exception 和 Error。

2.2Throwable 类是所有异常和错误的超类,下面有 Error 和 Exception 两个子类分别表示错误和异常。其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

1、Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。

2、Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。

这里暂时不讨论关于 Error 类型的异常处理,因为它们通常是灾难性的致命错误,不是程序可以控制的。接下来将讨论 Exception 类型的异常处理。

运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。

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

表 1 和表 2 分别列出了 java.lang 中定义的运行时异常和非运行时异常的类型及作用。   

表 1 Java中常见运行时异常

异常类型

说明

ArithmeticException

算术错误异常,如以零做除数

ArraylndexOutOfBoundException

数组索引越界

ArrayStoreException

向类型不兼容的数组元素赋值

ClassCastException

类型转换异常

IllegalArgumentException

使用非法实参调用方法

lIIegalStateException

环境或应用程序处于不正确的状态

lIIegalThreadStateException

被请求的操作与当前线程状态不兼容

IndexOutOfBoundsException

某种类型的索引越界

NullPointerException

尝试访问 null 对象成员,空指针异常

NegativeArraySizeException

再负数范围内创建的数组

NumberFormatException

数字转化格式异常,比如字符串到 float 型数字的转换无效

TypeNotPresentException

类型未找到

表 2 Java常见非运行时异常

异常类型

说明

ClassNotFoundException

没有找到类

IllegalAccessException

访问类被拒绝

InstantiationException

试图创建抽象类或接口的对象

InterruptedException

线程被另一个线程中断

NoSuchFieldException

请求的域不存在

NoSuchMethodException

请求的方法不存在

ReflectiveOperationException

与反射有关的异常的超类

二、Error和Exception的异同

        Error(错误)和 Exception(异常)都是 java.lang.Throwable 类的子类,在 Java 代码中只有继承了 Throwable 类的实例才能被 throw 或者 catch。

        Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类,Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获,进行相应的处理。Error 是指正常情况下不大可能出现的情况,绝大部分的 Error 都会导致程序处于非正常、不可恢复状态。所以不需要被开发者捕获。

        Error 错误是任何处理技术都无法恢复的情况,肯定会导致程序非正常终止。并且 Error 错误属于未检查类型,大多数发生在运行时。Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。不检查异常就是所谓的运行时异常,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。

        如下是常见的 Error 和 Exception:

        1)运行时异常(RuntimeException): ​               NullPropagation:空指针异常; ​              ClassCastException:类型强制转换异常 ​               IllegalArgumentException:传递非法参数异常 ​             IndexOutOfBoundsException:下标越界异常 ​               NumberFormatException:数字格式异常

        2)非运行时异常: ​             ClassNotFoundException:找不到指定 class 的异常 IOException:IO 操作异常

        3)错误(Error): NoClassDefFoundError:找不到 class 定义异常 ​            StackOverflowError:深递归导致栈被耗尽而抛出的异常 ​            OutOfMemoryError:内存溢出异常

三、异常处理机制

        Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。

        Java 的异常处理机制提供了一种结构性和控制性的方式来处理程序执行期间发生的事件。异常处理的机制如下:

1、在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。

2、对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。

四、try catch语句

## 1、try catch

Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。

实例:

public class Demo01 {

            public static void main(String[] args) {

                /**

                 * try catch语句,捕获异常并且处理异常

                 * 语法:

                 *      try{

                 *          代码块(可能出现错误的代码)

                 *      } catch(异常的类型){

                 *          处理异常的逻辑代码块

                 *      } catch () {

                 *

                 *      }.....

                 */

  

                int[] arr = new int[1];

  

  //        System.out.println(arr[30]);

  

                try{

                    // System.out.println(arr[30]);

                    int num = 9 / 0;

                }catch(NullPointerException e){

                    System.out.println("空指针异常");

                    System.out.println(e.getMessage());

                    e.printStackTrace();

                }catch (ArrayIndexOutOfBoundsException e){

                    System.out.println("程序出错了, 数组下标越界异常");

                    e.printStackTrace();// 打印异常栈信息

                    System.out.println(e.getMessage());

                }catch (Exception e) {

                    System.out.println("未知的异常");

                    System.out.println(e.getMessage());

                }

            }

        }

在以上语法中,把可能引发异常的语句封装在 try 语句块中,用以捕获可能发生的异常。catch 后的( )里放匹配的异常类,指明 catch 语句可以处理的异常类型,发生异常时产生异常类的实例化对象。

如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。

如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。

**注意:**try...catch 与 if...else 不一样,try 后面的花括号{ }不可以省略,即使 try 块里只有一行代码,也不可省略这个花括号。与之类似的是,catch 块后的花括号{ }也不可以省略。另外,try 块里声明的变量只是代码块内的局部变量,它只在 try 块内有效,其它地方不能访问该变量。

在上面语法的处理代码块 1 中,可以使用以下 3 个方法输出相应的异常信息。

printStackTrace() 方法:指出异常的类型、性质、栈层次及出现在程序中的位置。

getMessage() 方法:输出错误的性质。

toString() 方法:给出异常的类型与性质。

2、多重catch语句

如果 try 代码块中有很多语句会发生异常,而且发生的异常种类又很多。那么可以在 try 后面跟有多个 catch 代码块。多 catch 代码块语法如下:

在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。

**注意:**当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。

五、try catch finally语句

在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。

Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。

所以为了确保一定能回收 try 块中打开的物理资源,异常处理机制提供了 finally 代码块,并且 Java 7 之后提供了自动资源管理(Automatic Resource Management)技术。

使用 try-catch-finally 语句时需注意以下几点:

1、异常处理语法结构中只有 try 块是必需的,也就是说,如果没有 try 块,则不能有后面的 catch 块和 finally 块;

2、catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其中之一,也可以同时出现;

3、可以有多个 catch 块,捕获父类异常的 catch 块必须位于捕获子类异常的后面;

4、不能只有 try 块,既没有 catch 块,也没有 finally 块;

5、多个 catch 块必须位于 try 块之后,finally 块必须位于所有的 catch 块之后。

6、finally 与 try 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。

7、try catch finally 语句块的执行情况可以细分为以下 3 种情况:

1、如果 try 代码块中没有拋出异常,则执行完 try 代码块之后直接执行 finally 代码块,然后执行 try catch finally 语句块之后的    语句。

2、如果 try 代码块中拋出异常,并被 catch 子句捕捉,那么在拋出异常的地方终止 try 代码块的执行,转而执行相匹配的 catch         代码块,之后执行 finally 代码块。如果 finally 代码块中没有拋出异常,则继续执行 try catch finally 语句块之后的语句;如果 finally         代码块中拋出异常,则把该异常传递给该方法的调用者。

3、如果 try 代码块中拋出的异常没有被任何 catch 子句捕捉到,那么将直接执行 finally 代码块中的语句,并把该异常传递给该     方法的调用者。

除非在 try 块、catch 块中调用了退出虚拟机的方法System.exit(int status),否则不管在 try 块或者 catch 块中执行怎样的代码,出现怎样的情况,异常处理的 finally 块总会执行。

通常情况下不在 finally 代码块中使用 return 或 throw 等导致方法终止的语句,否则将会导致 try 和 catch 代码块中的 return 和 throw 语句失效

六、声明和抛出异常

## 1、throws 声明异常

当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。throws 具体格式如下:

```java

returnType method_name(paramList) throws Exception 1,Exception2,…{…}

```

其中,returnType 表示返回值类型;method_name 表示方法名;paramList 表示参数列表;Exception 1,Exception2,… 表示异常类。

如果有多个异常类,它们之间用逗号分隔。这些异常类可以是方法中调用了可能拋出异常的方法而产生的异常,也可以是方法体中生成并拋出的异常。

1.2、使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。

1.3、方法重写时声明抛出异常的限制

        使用 throws 声明抛出异常时有一个限制,是方法重写中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多

注意:所以在编写类继承代码时要注意,子类在重写父类带 throws 子句的方法时,子类方法声明中的 throws 子句不能出现父类对应方法的 throws 子句中没有的异常类型,因此 throws 子句可以限制子类的行为。也就是说,子类方法拋出的异常不能超过父类定义的范围。

  1. throw抛出异常

与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,其语法格式如下:

```java

throw ExceptionObject;

```

​        其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。例如,以下语句在编译时将会产生语法错误:

```java

throw new String("拋出异常");    // String类不是Throwable类的子类

```

​        当 throw 语句执行时,它后面的语句将不执行,此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的异常处理程序。如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。这样逐层向上,直到最外层的异常处理程序终止程序并打印出调用栈情况。

​        throw 关键字不会单独使用,它的使用完全符合异常的处理机制,但是,一般来讲用户都在避免异常的产生,所以不会手工抛出一个新的异常类的实例,而往往会抛出程序中已经产生的异常类的实例。

2.2、throws 关键字和 throw 关键字在使用上的几点区别如下:**

        1、throws 用来声明一个方法可能抛出的所有异常信息,表示出现异常的一种可能性,但并不一定会发生这些异常;throw 则是指拋出的一个具体的异常类型,执行 throw 则一定抛出了某种异常对象。

通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。

# 七、自定义异常

​        如果 Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。

​        自定义异常的语法形式为:

```java

<class><自定义异常名><extends><Exception>

```

​        在编码规范上,一般将自定义异常类的类名命名为 XXXException,其中 XXX 用来代表该异常的作用。

​        自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。

        2、throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。

2、throw 拋出异常与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,其语法格式如下:

```java

throw ExceptionObject;

       其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。例如,以下语句在编译时将会产生语法错误:

```java

throw new String("拋出异常");    // String类不是Throwable类的子类

```

       当 throw 语句执行时,它后面的语句将不执行,此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的异常处理程序。如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。这样逐层向上,直到最外层的异常处理程序终止程序并打印出调用栈情况。

       throw 关键字不会单独使用,它的使用完全符合异常的处理机制,但是,一般来讲用户都在避免异常的产生,所以不会手工抛出一个新的异常类的实例,而往往会抛出程序中已经产生的异常类的实例

02集合、泛型

一、集合简介

为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。

集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。

Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。

在 图 1 和图 2 中,黄色块为集合的接口,蓝色块为集合的实现类。下表 介绍了这些接口的作用。

接口名称

作 用

Iterator 接口

集合的输出接口,主要用于遍历输出(即迭代访问)Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。

Collection 接口

是 List、Set 和 Queue 的父接口,是存放一组单值的最大接口。所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。

Queue 接口

Queue 是 Java 提供的队列实现,有点类似于 List。

Dueue 接口

是 Queue 的一个子接口,为双向队列。

List 接口

是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。

Set 接口

不能包含重复的元素。

Map 接口

是存放一对值的最大接口,即接口中的每个元素都是一对,以 key➡value 的形式保存。

        对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。下表介绍了集合中这些常用的实现类。

| 类名称     | 作用                                                         |

| HashSet    | 为优化査询速度而设计的 Set。它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,实现比较简单 |

| TreeSet    | 实现了 Set 接口,是一个有序的 Set,这样就能从 Set 里面提取一个有序序列 |

| ArrayList  | 一个用数组实现的 List,能进行快速的随机访问,效率高而且实现了可变大小的数组 |

| ArrayDueue | 是一个基于数组实现的双端队列,按“先进先出”的方式操作集合元素 |

| LinkedList | 对顺序访问进行了优化,但随机访问的速度相对较慢。此外它还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)来用 |

| HsahMap    | 按哈希算法来存取键对象                                       |

| TreeMap    | 可以对键对象进行排序                                         |

二、Collection接口

           Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

        接下来将介绍 Collection 接口中常用的方法,如下表所示。

方法名称

说明

boolean add(E e)

向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型

boolean addAll(Collection c)

向集合中添加集合 c 中的所有元素,如果集合对象被添加操作改变了,则返回 true。

void clear()

清除集合中的所有元素,将集合长度变为 0。

boolean contains(Object o)

判断集合中是否存在指定元素

boolean containsAll(Collection c)

判断集合中是否包含集合 c 中的所有元素

boolean isEmpty()

判断集合是否为空

Iterator<E>iterator()

返回一个 Iterator 对象,用于遍历集合中的元素

boolean remove(Object o)

从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。

boolean removeAll(Collection c)

从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。

boolean retainAll(Collection c)

从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),如果该操作改变了调用该方法的集合,则该方法返回 true。

int size()

返回集合中元素的个数

Object[] toArray()

把集合转换为一个数组,所有的集合元素变成对应的数组元素。

        注意:以上方法完全来自于 Java API 文档,可自行参考 API 文档来查阅这些方法的详细信息。无需硬性记忆这些方法,可以和实际生活结合记忆。集合类就像容器,现实生活中容器的功能,就是添加对象、删除对象、清空容器和判断容器是否为空等,集合类为这些功能都提供了对应的方法。

**注意:**retainAll( ) 方法的作用与 removeAll( ) 方法相反,即保留两个集合中相同的元素,其他全部删除。

​        编译上面程序时,系统可能输出一些警告提示,这些警告是提示用户没有使用泛型来限制集合里的元素类型,读者暂时不要理会这些警告,后面我们会详细介绍泛型编程。

​        在传统模式下,把一个对象“丢进”集合中后,集合会忘记这个对象的类型。也就是说,系统把所有的集合元素都当成 Object 类型。从 Java 5 以后,可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型。

实例:

public class Demo01 {

    public static void main(String[] args) {

        /**

         * collection接口

         */

        // 定义集合

        Collection col = new ArrayList();

        // 添加集合元素

        col.add(1);

        col.add(new Date());

        col.add("Hello Collection");

        // 获取集合长度

        int len = col.size();

        System.out.println(len);

        // 去除集合所有元素

        // col.clear();

        // System.out.println(col.size());

        // 判断集合中是否存在指定元素

        boolean b = col.contains("Hello java");

        System.out.println(b);

        // 判断集合是否为空

        boolean empty = col.isEmpty();

        System.out.println(empty);

        // 返回一个 Iterator 对象,用于遍历集合中的元素

        Iterator iterator = col.iterator();

        // 遍历集合

        while (iterator.hasNext()) {

            // 取元素

            System.out.println(iterator.next());

        }

        // 从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,

        // 该方法只删除第一个符合条件的元素,该方法将返回 true。

        col.remove(1);

        System.out.println("-------------------------");

        for (Object o : col) {

            System.out.println(o.toString());

        }

        // 把集合转换为一个数组,所有的集合元素变成对应的数组元素。

        Object[] objects = col.toArray();

        System.out.println(objects.length);

    }

}

public class Demo02 {

    public static void main(String[] args) {

        /**

         * 利用forEach语法遍历集合元素

         */

        List<String> lt = new LinkedList<>();

        lt.add("Java");

        lt.add("C");

        lt.add("SQL");

        // 遍历list集合

        lt.forEach(s -> System.out.println(s));

        // 创建set集合

        Set<Student> stus = new HashSet<>();

        stus.add(new Student(1, "大锤", "123"));

        Student student = new Student();

        student.setId(2);

        student.setName("令狐冲");

        student.setPwd("456");

        stus.add(student);

        stus.add(new Student(3, "任我行", "789"));

        // 遍历set集合

        stus.forEach(set -> System.out.println(set));

        // 遍历list集合并且删除元素

        List<String> list = new LinkedList<>();

        list.add("Java");

        list.add("C");

        list.add("SQL");

        System.out.println("遍历集合之前:" + list);

        System.out.println("------------------------------");

        // 获取迭代器对象

        Iterator<String> iterator = list.iterator();

        // 循环遍历集合

        while (iterator.hasNext()) {

            // 取出元素

            System.out.println(iterator.next());

            // 将取出来的元素删除

            iterator.remove();

        }

        System.out.println("------------------------------");

        System.out.println("遍历集合之后:" + list);

        /**

         * Java 8 为 Iterator 引入了一个 forEachRemaining(Consumer action) 默认方法,

         * 该方法所需的 Consumer 参数同样也是函数式接口。当程序调用 Iterator 的

         * forEachRemaining(Consumer action) 遍历集合元素时,程序会依次将集合元素传给

         * Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。

         */

        List<String> list1 = new LinkedList<>();

        list1.add("Java");

        list1.add("C");

        list1.add("SQL");

        // 获取迭代器对象

        Iterator<String> itr = list1.iterator();

        // 利用lambda遍历迭代器对象

        itr.forEachRemaining(result -> System.out.println(result));

    }

}

# 三、List集合

​        List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。

​        List 实现了 Collection 接口,它主要有两个常用的实现类:ArrayList 类和 LinkedList 类。

## 1、ArrayList 类

​        ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。

ArrayList 类的常用构造方法有如下两种重载形式:

1、ArrayList():构造一个初始容量为 10 的空列表。

2、ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返      回它们的顺序排列的。

ArrayList 类除了包含 Collection 接口中的所有方法之外,还包括 List 接口中提供的如下表所示的方法。

| 方法名称                                    | 说明                                                         |

| ------------------------------------------- | ------------------------------------------------------------ |

| E get(int index)                            | 获取此集合中指定索引位置的元素,E 为集合中元素的数据类型     |

| int index(Object o)                         | 返回此集合中第一次出现指定元素的索引,如果此集合不包含该元 素,则返回 -1 |

| int lastIndexOf(Object o)                   | 返回此集合中最后一次出现指定元素的索引,如果此集合不包含该 元素,则返回 -1 |

| E set(int index, Eelement)                  | 将此集合中指定索引位置的元素修改为 element 参数指定的对象。 此方法返回此集合中指定索引位置的原元素 |

| List<E> subList(int fromlndex, int tolndex) | 返回一个新的集合,新集合中包含 fromlndex 和 tolndex 索引之间 的所有元素。包含 fromlndex 处的元素,不包含 tolndex 索引处的 元素 |

​        **注意:**当调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素,也就是说这个方法不会改变 List 集合的长度。

2LinkedList

           LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。

        LinkedList 类除了包含 Collection 接口和 List 接口中的所有方法之外,还特别提供了表 2 所示的方法。

方法名称

说明

void addFirst(E e)

将指定元素添加到此集合的开头

void addLast(E e)

将指定元素添加到此集合的末尾

E getFirst()

返回此集合的第一个元素

E getLast()

返回此集合的最后一个元素

E removeFirst()

删除此集合中的第一个元素

E removeLast()

删除此集合中的最后一个元素

## 3、ArrayList 类和 LinkedList 类的区别

        ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同。

        ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。

        对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。

        不同的结构对应于不同的算法,有的考虑节省占用空间,有的考虑提高运行效率,对于程序员而言,它们就像是“熊掌”和“鱼肉”,不可兼得。高运行速度往往是以牺牲空间为代价的,而节省占用空间往往是以牺牲运行速度为代价的。

 四、Set集合

        Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。

        Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。

## 1、HashSet 类

        HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。

        HashSet 具有以下特点:

1、不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。

2、HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。

3、集合元素值可以是 null。

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。

也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。

在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。

1、HashSet():构造一个新的空的 Set 集合。

2、HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示         HashSet 的父类,即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。

## 2TreeSet

        TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。

        TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。

        下表列举了 JDK 类库中实现 Comparable 接口的类,以及这些类对象的比较方式。

| 类                                                           | 比较方式                                  |

| ------------------------------------------------------------ | ----------------------------------------- |

| 包装类(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) | 按数字大小比较                            |

| Character                                                    | 按字符的 Unicode 值的数字大小比较         |

| String                                                       | 按字符串中字符的 Unicode 值的数字大小比较 |

        TreeSet 类除了实现 Collection 接口的所有方法之外,还提供了如表 2 所示的方法。

| 方法名称                                       | 说明                                                         |

| ---------------------------------------------- | ------------------------------------------------------------ |

| E first()                                      | 返回此集合中的第一个元素。其中,E 表示集合中元素的数据类型   |

| E last()                                       | 返回此集合中的最后一个元素                                   |

| E poolFirst()                                  | 获取并移除此集合中的第一个元素                               |

| E poolLast()                                   | 获取并移除此集合中的最后一个元素                             |

| SortedSet<E> subSet(E fromElement,E toElement) | 返回一个新的集合,新集合包含原集合中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象 |

| SortedSet<E> headSet<E toElement〉             | 返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。 不包含 toElement 对象 |

| SortedSet<E> tailSet(E fromElement)            | 返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对 象。包含 fromElement 对象 |

        **注意:**表面上看起来这些方法很多,其实很简单。因为 TreeSet 中的元素是有序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并提供了 3 个从 TreeSet 中截取子 TreeSet 的方法。

# 五、Map集合

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。

Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。

Map 接口中提供的常用方法如下表所示。

| 方法名称                                 | 说明                                                         |

| ---------------------------------------- | ------------------------------------------------------------ |

| void clear()                             | 删除该 Map 对象中的所有 key-value 对。                       |

| boolean containsKey(Object key)          | 查询 Map 中是否包含指定的 key,如果包含则返回 true。         |

| boolean containsValue(Object value)      | 查询 Map 中是否包含一个或多个 value,如果包含则返回 true。   |

| V get(Object key)                        | 返回 Map 集合中指定键对象所对应的值。V 表示值的数据类型      |

| V put(K key, V value)                    | 向 Map 集合中添加键-值对,如果当前 Map 中已有一个与该 key 相等的 key-value 对,则新的 key-value 对会覆盖原来的 key-value 对。 |

| void putAll(Map m)                       | 将指定 Map 中的 key-value 对复制到本 Map 中。                |

| V remove(Object key)                     | 从 Map 集合中删除 key 对应的键-值对,返回 key 对应的 value,如果该 key 不存在,则返回 null |

| boolean remove(Object key, Object value) | 这是 [Java](http://c.biancheng.net/java/) 8 新增的方法,删除指定 key、value 所对应的 key-value 对。如果从该 Map 中成功地删除该 key-value 对,该方法返回 true,否则返回 false。 |

| Set entrySet()                           | 返回 Map 集合中所有键-值对的 Set 集合,此 Set 集合中元素的数据类型为 Map.Entry |

| Set keySet()                             | 返回 Map 集合中所有键对象的 Set 集合                         |

| boolean isEmpty()                        | 查询该 Map 是否为空(即不包含任何 key-value 对),如果为空则返回 true。 |

| int size()                               | 返回该 Map 里 key-value 对的个数                             |

| Collection values()                      | 返回该 Map 里所有 value 组成的 Collection                    |

Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。

 六、遍历Map集合

Map 集合的遍历与 List 和 Set 集合不同。Map 有两组值,因此遍历时可以只遍历值的集合,也可以只遍历键的集合,也可以同时遍历。Map 以及实现 Map 的接口类(如 HashMap、TreeMap、LinkedHashMap、Hashtable 等)都可以用以下几种方式遍历。

1)在 for 循环中使用 entries 实现 Map 的遍历(最常见和最常用的)。

public static void main(String[] args) {

   Map<String, String> map = new HashMap<String, String>();

   map.put("Java教程", "http://www.zpark.com/java/");

   map.put("C语言教程", "http://www.zpark.com/c/");

   

   for (Map.Entry<String, String> entry : map.entrySet()) {

       String mapKey = entry.getKey();

       String mapValue = entry.getValue();

       System.out.println(mapKey + ":" + mapValue);

  }

}

        2)使用 for-each 循环遍历 key 或者 values,一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。

Map<String, String> map = new HashMap<String, String>();

map.put("Java教程", "http://www.zpark.com/java/");

map.put("C语言教程", "http://www.zpark.com/c/");



// 打印键集合

for (String key : map.keySet()) {

   System.out.println(key);

}



// 打印值集合

for (String value : map.values()) {

   System.out.println(value);

}

        3)使用迭代器(Iterator)遍历

Map<String, String> map = new HashMap<String, String>();

map.put("Java教程", "http://www.zpark.com/java/");

map.put("C语言教程", "http://www.zpark.com/c/");



Iterator<Entry<String, String>> entries = map.entrySet().iterator();



while (entries.hasNext()) {

   Entry<String, String> entry = entries.next();

   String key = entry.getKey();

   String value = entry.getValue();

   System.out.println(key + ":" + value);

}

        4)通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作。

for(String key : map.keySet()){

   String value = map.get(key);

   System.out.println(key+":"+value);

}

七、Collections

Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。

实例:

public class Demo01 {

    public static void main(String[] args) {

        List<String> lt = new ArrayList<>();

        lt.add("java");

        lt.add("C");

        lt.add("C++");

        lt.add("Python");

        // 创建set集合

        Set set = new HashSet<>();

        // 将集合lt放到set集合中

        // 将所有指定的元素添加到指定的集合。

        Collections.addAll(set, lt);

        set.add(lt);

        // 查看set集合的长度

        System.out.println(set.size());

        System.out.println(set);

        Set<List<String>> st = new HashSet<>();

    }

}

实例2:

public class Demo02 {

    public static void main(String[] args) {

        /**

         * 用指定的元素替换指定列表的所有元素。

         */

        List<String> lt = new ArrayList<>();

        lt.add("java");

        lt.add("C");

        lt.add("C++");

        lt.add("Python");

        System.out.println("替换之前:" + lt);

        // 用指定的元素替换指定列表的所有元素。

//        Collections.fill(lt, "SQL");

//

//        System.out.println("替换之后:" + lt);

        /**

         * 根据其元素的 自然顺序返回给定集合的最大元素。

         */

        String max = Collections.max(lt);

        System.out.println(max);

        /**

         * 根据其元素的 自然顺序返回给定集合的最小元素。

         */

        String min = Collections.min(lt);

        System.out.println(min);

        /**

         * 将列表中一个指定值的所有出现替换为另一个。

         */

        Collections.replaceAll(lt, "java", "JAVA");

        System.out.println(lt);

        /**

         * 反向输出集合内容

         */

        Collections.reverse(lt);

        System.out.println(lt);

        /**

         * 默认排序集合

         */

        Collections.sort(lt);

        System.out.println(lt);

        /**

         * 根据指定的比较器引起的顺序对指定的列表进行排序。

         */

        Collections.sort(lt, new Comparator<String>() {

            @Override

            public int compare(String o1, String o2) {

                return o2.length() - o1.length();

            }

        });

        System.out.println(lt);

        /**

         * 交换指定列表中指定位置的元素。

         */

        Collections.swap(lt, 0, lt.size() - 1);

        System.out.println(lt);

    }

}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值