Java核心编程

第一节

1、什么是Java?

Java 是一种面向对象的语言,它对对象中的类、对象、继承、封装、多态、接口、包等均有很好的支持。为了简单起见,Java 只支持类之间的单继承,但是可以使用接口来实现多继承。

2、标识符由数字、字母、下划线(_)、及$组成 如:a12ghdg数字不能作为标识符的开始,如:1ac

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

用户自定义标识符:是由用户按标识符构成规则生成的非保留字的标识符,如abc、name 就是一个标识符。

4、Java类的命名规则:

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

如:HelloWorldDemo01

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

如:helloWorld,dayMyYou

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

6、变量命名:

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

例:

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

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

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

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

age = 56;

name = "张三";

int 2num;  // 非法命名

7、变量的作用域:

全局变量:定义在方法(函数)和代码块之外的变量 。

局部变量: 定义在方法或者代码块之间的变量。

例:

// 定义全局变量

    int age; // 全局变量

    String name = "大锤";

    // 静态变量

    static int number;

    static double price = 1399.99;

    public static void main(String[] args){

        // 定义局部变量

        int age;

        if (true) {

            String location;

        }

//        System.out.println(location);

    }

}

8、常量命名规则:

1)常量名字应该全部大写 。

2)当常量名由多个单词组成时采用下划线分割,如:HTTP_CODE、HTTP_STATUS_CODE。

例:

// 成员常量

final double PI = 3.14159;

// 静态常量,使用 final static关键字修饰

final static float PRICE = 99.99F;

// 定义常量,使用关键字final

        final int PI = 3;

        // 常量一旦被定义赋值,则不可再被修改

//        pi = 6; Variable 'name' might already have been assigned to

        // 局部常量

        final String NAME;

        NAME = "大锤";

//        final static long n2 = 3L; 错误写法

9、Java中的数据类型:

1、基本数据类型(八种)

byte(字节型)、short(短整型)、int(整形)、long(长整型)、float(单精度浮点型)、double(双精度浮点型)char(字符型)、boolean(布尔型)

类型名称

关键字

占用内存

字节型

byte

1 字节

短整型

short

2 字节

整型

int

4 字节

长整型

long

8 字节

单精度浮点型

float

4 字节

双精度浮点型

double

8 字节

字符型

char

2 字节

布尔型

boolean

1 字节

2、引用数据类型

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

例:

// 字节型

byte by = 127;

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

// 短整型

short sh = 56;

// 整形

int num = 78946;

// 长整型,注意: 需要早长整型末尾添加L或者l。

long number = 56L;

// 单精度浮点型, 注意: 需要早长整型末尾添加F或者f

float fl = 3.14F;

float fl1 = 3.1415888888879874243246564564561129f; // 精度丢失

System.out.println(fl1);

// 双精度浮点型,java中小数默认是double类型

double db = 3.14159;

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

char ch1 = 3;

char ch2 = 'a';

char ch3 = 97;

System.out.println(ch1);

System.out.println(ch3);

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

boolean bool1 = true;

boolean bool2 = false;

// null类型

String str = null;

System.out.println(name);

//        int a = null; 报错

第二节

  1. Java基本数据类型
  2.   基本数据类型大小排行榜
  3.        byte    1字节
  4.        short   2字节
  5.        char    2字节
  6.        int     4字节
  7.        long    8字节
  8.        float   4字节
  9.        double  8字节

  1. Java数据类型的转换

自动转换:byte→short→int→long→float→double。

强制转换:double→float→longintshortbyte, shortchar, charshort.

 

 byte b1 = 25;

// 小类型数据转为大类型数据(自动转换)

short s1 = b1;

// int age = "";

short s2 = 654;

// 大类型数据转为小类型数据时需要强制转换(类型强转)

// 小类型  变量名  =  (需要转换的类型) 大类型;

// 注意:大类型数据转为小类型数据有可能会造成数据精度丢失和溢出

byte b2 = (byte) s2;

System.out.println(b2);

short类型与char类互相转化需要强制转换

short s3 = 97;

char ch1 = (char) s3;

System.out.println(ch1);

注意:两种数据类型彼此兼容,否则不能相互转换

基本数据类型不能与引用数据类型转换

 

//不能强转

//int num1 = (int) str1;

int num2 = 56;

//不能转换

//String str2 = (String) num2;

Date time = new Date();

String s1 = time.toString();

Demo01 d1 = new Demo01();

// d1 = (Demo01) time;

Demo02 d2 = (Demo02) new Object();

byte b1 = 12;

int num = 13;

byte b2;

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

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

b2 = (byte) (b1 + num);

第三节

、Java运算符

注意:+号可以用作算术运算,也可以做字符串拼接

      任何数据类型与字符串做 + 运算都会变为字符串

int num = 23;

String s1 = num + "";

System.out.println(s1);

1、一元运算符:

-:取反

++:自增

--:自减

int num1 = 56;

int num2 = -num1;

System.out.println(num2);

int num3 = 25;

//num3 = num3++;

num3++;//num3自增+1

System.out.println(num3);

++num3;//num3自增+1

System.out.println(num3);

int num4;

num4 = num3++;// 先将 num3 赋值给num4,然后num3再自增 1

System.out.println("num4 = "+num4+",num3 = "+num3);

int num5 = 25;

int num6;

num6 = ++num5;//先num5自增,然后再将自增之后的值赋值给 num6

System.out.println("num5 = "+num5+",num6 = "+num6);

2、二元运算符:

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

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

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

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

 

int num1 = 8;

int num2 = 74;

// 加法运算

int num3 = num1 + num2;

System.out.println(num3);

// 减法运算

num3 = num2 - num1;

System.out.println(num3);

// 乘法运算

num3 = num1 * num2;

System.out.println(num3);

// 除法运算

num3 = num2 / num1; // 求商

System.out.println(num3);

// 取余运算

num3 = num2 % num1;

System.out.println(num3);

3、算术赋值运算符:

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;

int num = 77;

num += 7;// num = num + 7; ---->  num = 77 + 7

System.out.println(num);

int num1 = 77;

num1 -= 7; // num1 = num1 - 7  -----> 77 - 7

System.out.println(num1);

int num2 = 77;

num2 *= 7; // num2 = num2 * 7  -----> 77 * 7

System.out.println(num2);

int num3 = 77;

num3 /= 7; // num3 = num3 / 7  -----> 77 / 7

System.out.println(num3);

int num4 = 77;

num4 %= 7; // num4 = num4 % 7  -----> 77 * 7

System.out.println(num4);

4、逻辑运算符:

java中逻辑运算符:

    与、或、非

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

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

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

       boolean bool1 = true;

       boolean bool2 = true;

       boolean bool3 = false;

       boolean bool4 = false;

       System.out.println("---------------&&(短路与)-----------------");

       System.out.println(bool1 && bool2);// true

       System.out.println(bool1 && bool3);// false

       System.out.println(bool3 && bool4);// false

       System.out.println("---------------||(短路或)-----------------");

       System.out.println(bool1 || bool2);// true

       System.out.println(bool1 || bool3);// true

       System.out.println(bool3 || bool4);// false

       System.out.println("----------非 ! --------------");

       System.out.println(!bool1);// false

       System.out.println(!bool4);// true

      

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

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

       

  System.out.println("**************************************************");

       int num = 7;

       System.out.println(false && num++ > 2);

       System.out.println(num);

       System.out.println(false & num++ > 2);

       System.out.println(num);

       System.out.println("**************************************************");

       int num1 = 7;

       System.out.println(true || num1++ > 2);

       System.out.println(num1);

       System.out.println(true | num1++ > 2);

       System.out.println(num1);

5、关系运算符:

       ==:等于

       !=:不等于

       >:大于

       <:小于

       >=:大于等于

       <=:小于等于

 

       注意:关系运算符的结果为布尔值类型

 

int num1 = 7777;

int num2 = 777;

System.out.println(num1 == num2);// false

System.out.println(num1 != num2);// true

System.out.println(num1 >= num2);// true

System.out.println(num1 <= num2);// false

System.out.println(num1 > num2);// true

System.out.println(num1 < num2);// false

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

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

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

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

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

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

// 创建扫描器对象

Scanner scan = new Scanner(System.in);

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

int age = scan.nextInt();

System.out.println();

System.out.println(age);

// 获取用户输入的小数

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

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

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

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

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

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

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

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

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

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

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

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

位移运算符:

位移运算符用来将操作数向某个方向(向左或者右)移动指定的二进制位数。

>>:右位移运算  如:8 >> 1  为 4

<<:左位移运算  如:4 << 1  为 8

int num = 16;

num = num / 2;

System.out.println(num);

num = num >> 1;

System.out.println(num);

int num1 = 2;

System.out.println(num1 << 2);

int num2 = -5;

System.out.println(num2 >> 1);

int num3 = -5;

System.out.println(num3 >>> 1);

第四节

一、分支结构

1、if语句

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

单分支结构:

              if () {

         

              }

单 if 语句仅能在满足条件时使用,而无法执行任何其他操作(停止)。而结合 else 语句的 if 可以定义两个操作,此时的 if…else 语句表示“如果条件正确则执行一个操作,否则执行另一个操作”。

双分支结构

          if语句:

               if (条件表达式) {

                   // 条件为true执行

                   // 代码块

               } else {

                   // 条件为false执行

               }

         

               案例:要求用户输入自己的年龄,然后判断其是否成年

         

        // 创建扫描器对象

        Scanner scan = new Scanner(System.in);

        // 提示用户输入自己的年龄

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

        int age = scan.nextInt();

        // 判断是否成年

        if (age >= 18) {

            System.out.println("恭喜你已经成年,可以上网");

        }else{

            System.out.println("信不信报警抓你");

            }

        

多分支结构:

               if () {

         

               }else if () {

         

               }else if () {

         

               }....{

         

               }else{

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

               }

if嵌套

               if(boolean){

                   if(boolean){

                       ....

                   }

               }else{

                   if(boolean){

                       ....

                   }

               }

  1. 三木运算语法(本质:还是if语句):

Java 提供了一个特别的三元运算符(也叫三目运算符)。三元运算符的符号表示为“?:”,使用该运算符时需要有三个操作数,因此称其为三目运算符

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

            当表达式结果true,执行表达式1,否则执行表达式2

            条件表达式 ? (条件表达式1 ? 表达式1 :表达式2) :(条件表达式2 ? 表达式1 :表达式2)

3、switch语句

         

                   switch () {

         

                  }

二、循环结构

1、while循环:

               语法:

                   while (循环条件) {

                       循环体

                   }

2、do.while循环

               语法:

                   do{

                       循环体

                   } while (循环条件);

while循环与do...while循环的区别:

               1、while是先判断后执行,do...while循环是先执行然后再判断

               2、do...while循环至少执行一次

          while循环与do...while的特点:都是循环执行某一语句,循环次数素不固定

3、for循环:

               语法:

                       for (表达式1; 表达式2; 表达式3) {

                           循环体

                       }

               特点:循环次数固定

4、foreach语法:

               for(迭代变量类型 变量的名字 :需要遍历(迭代)的对象){

                   语句块;

               }

递归算法:

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

递归算法:在函数或子过程的内部,直接或者间接地调用自己的算法。 递归算法的实质:是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。

递归算法解决问题的特点: (1) 递归就是在过程或函数里调用自身。  (2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。  (3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。  (4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。

四、中断结构

  1. break语句

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

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

  • 方法的定义和调用

首先方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。

返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。

方法名:是方法的实际名称。方法名和参数表共同构成方法签名。

参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。

方法体:方法体包含具体的语句,定义该方法的功能。

定义方法的语法:

           修饰符  返回值类型   方法名(参数1,参数2...) { 方法体... }

定义方法的语法:

           修饰符  返回值类型   方法名(参数1,参数2...) { 方法体... }

    // 自定义方法输出

    // 无返回值。有参数

    public static void print(String str){

        System.out.println(str);

    }

    // 定义有返回值的方法,但是没有参数

    int fun1(){

        // return关键字的作用:1、结束方法的执行。2、将方法的执行结果返回给调用者

        return 12;

    }

    // 有参有返回值

    String fun2(int[] array, double dou, long num, Date time){

        return "有参有返回值";

    }

    // 无参无返回值的方法

    void fun3(){

    }

方法(函数)的类型:

           1、无参无返回值

               修饰符  void  方法名(){ 方法体 }

           2、无参有返回值

               修饰符   返回值类型   方法名(){ 方法体}

           3、有参无返回值

               修饰符   void   方法名(参数1, 参数2,....){ 方法体}

           4、有参有返回值

               修饰符   返回值类型  方法名(参数1, 参数2,....){ 方法体}

方法调用

           1、通过 对象.方法名字(参数)

           2、类名.方法(参数)

           3、方法(参数)

// 调用非static关键字修饰的方法,语法:对象.方法(参数)

// 调用被static修饰的方法,语法:类名.方法(参数)

方法重载:

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

        注意:

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

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

        什么是程序?

        答:程序 = 算法 + 数据结构

// 固定参数的方法

    public void num(int num, double d){

    }

    // 可变参数的定义, 可变参数最终会被封装成一个数组

    public void fun1(int... num){

        for (int i : num) {

            System.out.println(i);

        }

    }

    public int fun2(String str, double... d){

        return 0;

    }

    // 可变参数必须位于参数列表的最后一个

    public String fun3(long... longs, String str, int num){

        return null;

}

  • 数组

1、数组的定义:

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

数组的声明:

2、访问数组:

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

获取数组的长度:arr.length

3、数组的四个基本特点:

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

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

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

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

3、数组初始化

1.静态初始化

除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。

2.动态初始化

数组定义与为数组元素分配空间并赋值的操作分开进行

3.数组的默认初始化

数组是引用类型,他的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化

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

5、遍历数组:

遍历数组的意思就是将数组的元素逐个取出来,通常我们使用循环进行遍历。

第五节

一、二维数组

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

两种初始化形式:

格式1、动态初始化

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

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

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

格式2、 静态初始化

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

本质:数组的元素还是数组

二、Arrays类

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

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

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

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

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

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

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

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

数组的复制:System.arraycopy(arr, start, dist, index, length)

案例:将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();

案例:将arr1数组进行复制,复制的范围下标为5之前的所有元素

            int arr3[] = Arrays.copyOf(arr1, 5);

            for (int j : arr3) {

                System.out.print(j + "\t");

            }

        System.out.println();

案例:将arr1数组中的元素复制到arr4数组中,复制到范围为下标1开始,到下标6结束

        int[] arr4 = Arrays.copyOfRange(arr1, 1, 6);

        for (int i : arr4) {

            System.out.print(i + "\t");

        }

数组扩容与缩容的实质:就是创建一个新的数组,新数组的长度比原来的数组(大,扩容,小,缩容)

对数组进行缩容

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

三、排序算法

十种常见排序算法可以分为两大类:

比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。

非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

相关概念

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。

不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。

空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

四、稀疏数组

稀疏数组介绍:

当一个数组中大部分元素为0时,或者为同一值的数组时,可以使用稀疏数组来保存该数组。

稀疏数组的处理方式是:

1、 记录数组一共有几行几列,有多少个不同的值。

2、 把具体有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模。

稀疏数组定义:

稀疏数组可以看做是普通数组的压缩,但是这里说的普通数组是值无效数据量远大于有效数据量的数组。

五、面向对象

面向过程与面向对象:

面向过程思想:

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

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

面向对象思想:

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

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

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

结构化程序设计的弊端:

​  1、缺乏对数据的封装

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

什么是面向对象:

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

什么是抽象的数据类型:

​  所谓抽象数据类型可以理解为:将不同类型的数据的集合组成一个整体,用来描述一种新的事务。如:对象类型数据

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

1、OOA:面向对象分析方法,是确定需求或者业务的角度,按照面向对象的思想来分析业务。

2、OOD:面向对象设计方法是OO方法中一个中间过渡环节。其主要作用是对OOA分析的结果作进一步的规范化整理,以便能够被OOP直接接受。

3、OOP:面向对象程序设计作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。

第六节

一、构造器

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

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

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

默认的构造方法:

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

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

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

构造方法的重载:

​   为了使用方便,可以对一个类定义多个构造方法,这些构造方法都有相同的名称,但是方法的参数列表不同,称为构造方法的重载。

通过构造方法初始化成员变量:

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

this关键字的使用:

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

在构造方法中,用来初始化成员变量的参数一般和成员变量取的名字相同,这样会有利于代码的可读性,但下列这种情况如果将this省略,就不能区分成员变量和参数了,所以必须加上this关键字。

构造方法(构造函数):

  每一个Java类必须包含构造方法,如果程序员没有手动添加构造方法,则编译器会在编译时添加一个默认的

 无参构造方法,关于构造方法的特点:1、构造方法的方法名字必须与类名完全相同(包括大小写必须相同),2、

 构造方法没有任何返回值,连void关键字也没有。

      构造方法的语法:

              修饰符   类名(参数){

                方法体....

              }

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

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

  • Java内存分析

1、堆、栈、方法区:

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

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

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

2、内存区域类型

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

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

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

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

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

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

三、对象数组

数组是对象:

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

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

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

引用类型数组的声明:

数组的元素可以是任意类型,当然也包括引用类型。

注意:**new  Cell[4];实际是分配了4个空间用于存放4个Cell类型的引用,并不是分配了4给Cell类型的对象。

引用类型数组的初始化:

(1)引用类型数组的默认初始值但是null。

(2)如果希望每一个元素都指向具体的对象,需要针对每一个数组元素进行new运算。

四、访问控制修饰符

1、public和private

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

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

2、 protected和默认访问控制

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

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

3、访问控制修饰类

(1)对于类的修饰可以使用public和默认的方式,public修饰的类可以被任意一个类使用,默认方法控制的类只能被同包中的类使用。

(2)protected和private可以用于修饰内部类

 4、访问控制符修饰成员变量

五、封装

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

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

封装的意义:

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

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

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

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

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

六、继承

 1、extends关键字

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

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

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

 2、继承中的构造方法

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

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

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

1、一个子类的对象可以向上造型为父类的类型,即,定义父类型的引用可以指向子类型的对象。

2、 父类的引用可以指向子类的对象,但是通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分。

七、super关键字

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、遵循"两同两小一大"原则:------------了解

两同:

1、方法名称相同

2、参数列表相同

两小:

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

a、void时,必须相同

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

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

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

一大:

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

重写与重载的区别

重写(Override):

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

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

重载(Overload):

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

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

九、多态

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

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

多态存在的条件:

1、 有继承关系

2、 子类重写父类的方法

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

多态的意义:

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

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

多态的表现形式:

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

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

第七节

一、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块属于类的代码块,在类加载期间指向代码块,只执行一次,可以用来在软件中加载静态资源。

语法:

          static {

               静态代码块

          }

 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常量会在编译期被替换。

二、抽象类

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

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

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

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

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

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

抽象方法:

1、由abstract修饰

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

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

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

1抽象方法没有方法体

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

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

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

抽象类:

1、由abstract修饰

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

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

3、抽象类不能被实例化

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

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

​          (2)、也声明为抽象类----------一般不这么做

5、抽象类的意义:

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

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

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

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

设计规则:

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

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

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

三、接口

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

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

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

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

接口与类相似点:

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

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

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

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

接口与类的区别:

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

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 以后,接口里可以有静态方法和方法体了。

四、内部类

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应用程式中快速创建事件处理程式。

五、枚举

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

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

1、声明枚举

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

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

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

 2、枚举类

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

第八节

String字符串处理

一、Java定义字符串

字符串是Java中特殊的类,使用方法像一般的基本数据类型,被广泛应用在Java编程中。Java没有内置的字符串类型,而是在标准Java类库中提供了一个 String类来创建和操作字符串。

在Java中定义一个字符串最简单的方法是用双引号把它包围起来。这种用双引号括起来的一串字符实际上都是String对象,如字符串“Hello”在编译后即成为String对象。因此也可以通过创建String类的实例来定义字符串。

不论使用哪种形式创建字符串,字符串对象一旦被创建,其值是不能改变的,但可以使用其他变量重新赋值的方式进行更改。

1、直接定义字符串

直接定义字符串是指使用双引号表示字符串中的内容,例如“Hello Java”、“Java编程”等。具体方法是用字符串常量String对象,示例如下:

2、使用String类定义

前面的学习中我们提到在Java中每个双引号定义的字符串都是一个String类的对象。因此,可以通过使用String类的构造方法来创建字符串,该类位于java.lang包中。

String类的构造方法有多种重载形式,每种形式都可以定义字符串。接下来介绍最常用的几种形式。

1、String()

初始化一个新创建的String对象,表示一个空字符序列。

  1. String(String original)

   初始化一个新的String对象,使其表示一个与参数相同的字符序列。

3. String(charl ]value)

分配一个新的字符串,将参数中的字符数组元素全部变为字符串。该字符数组的内容已被复制,后续对字符数组的修改不会影响新创建的字符串。

4. String(char, value,int offset,int count) I

分配一个新的String,它包含来自该字符数组参数一个子数组的字符。offset 参数是子数组第一个字符的索引,count参数指定子数组的长度。该子数组的内容已被赋值,后续对字符数组的修改不会影响新创建的字符串。

sChar变量的值是字符串“ello”。该构造方法使用字符数组中的部分连续元素来创建字符串对象。offset 参数指定起始索引值,count指定截取元素的个数。创建字符串对象后,即使在后面修改了a数组中第2个元素的值,对sChar的值也没有任何影响。

二、String和int的相互转换

1、String转换为int

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

(1)、 Integer.parselnt(str)

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

代码示例:

public static void main(String[] args) (

String str="123";

int n = 0;

//第一种转换方法:Integer.parseInt(str)

n = Integer,parseInt(str);

system.out.print1n("Integer.parseInt(str):"+ n);

// 第二种转换方法:Integer.valueof(str).intValue()

n =0;

n = Integer.valueof(str).intValue();

System.out.print1n("Integer.parseInt(str):"+ n);

注:在String转换int时,String的值一定是整数,否则会报数字转换异常(java.lang.NumberFormatException)

2、int转换为String

整型int转 String字符串类型有以下3种方法:

1、String s= String.valueOf(i);

2、String s= Integer.toString(i);

3、 String s=""+ i;

三、字符串的拼接

1、使用连接运算符“+”

与绝大多数的程序设计语言一样,Java语言允许使用“+”号连接(拼接)两个字符串。“+”运算符是最简单、最快捷,也是使用最多的字符串连接方式。在使用“+”运算符连接字符串和int型(或double型)数据时,“+”将int (或double)型数据自动转换成String类型。

2、使用 concat()方法

在Java中,String类的concat()方法实现了将一个字符串连接到另一个字符串的后面。concat)方法语法格式:字符串 1.concat(字符串2);

执行结果是字符串2 被连接到字符串1后面,形成新的字符串。

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

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

字符串名.length();

五、字符串大小写转换

将字符串转为大写

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

将字符串转小写

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

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

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

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

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

1、substring(int beginIndex) 形式

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

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

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

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

字符串截取

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

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

第九节

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

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

1、substring(int beginIndex) 形式

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

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

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

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

字符串截取

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

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

String 类的 split() 方法可以按指定的分割符对目标字符串进行分割,分割后的内容存放在字符串数组中。

案例:截取新闻标题

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

二、字符串的替换

 1、replace() 方法

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

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

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

案例:替换敏感字符

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

三、字符串比较

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

1、equals() 方法

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

2、equalsIgnoreCase() 方法

equalsIgnoreCase() 方法的作用和语法与 equals() 方法完全相同,唯一不同的是 equalsIgnoreCase() 比较时不区分大小写。当比较两个字符串时,它会认为 A-Z 和 a-z 是一样的。

3、equals()与==的比较

  理解 equals() 方法和`==`运算符执行的是两个不同的操作是重要的。如同刚才解释的那样,equals() 方法比较字符串对象中的字符。而`==`运算符比较两个对象引用看它们是否引用相同的实例。

四、字符串查找

在给定的字符串中查找字符或字符串是比较常见的操作。字符串查找分为两种形式:一种是在字符串中获取匹配字符(串)的索引值,另一种是在字符串中获取指定索引位置的字符。

根据字符查找

String 类的 indexOf() 方法和 lastlndexOf() 方法用于在字符串中获取匹配字符(串)的索引值。

1、indexOf() 方法

indexOf() 方法用于返回字符(串)在指定字符串中首次出现的索引位置,如果能找到,则返回索引值,否则返回 -1。

其中,str 表示指定字符串;value 表示待查找的字符(串);fromIndex 表示查找时的起始索引,如果不指定 fromIndex,则默认从指定字符串中的开始位置(即 fromIndex 默认为 0)开始查找。

2、lastlndexOf() 方法

lastIndexOf() 方法用于返回字符(串)在指定字符串中最后一次出现的索引位置,如果能找到则返回索引值,否则返回 -1。

五、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 对象中追加字符串。

该方法的作用是追加内容到当前 StringBuffer 对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer 对象的内容也发生了改变

3、替换字符

StringBuffer 类的 setCharAt() 方法用于在字符串的指定索引位置替换一个字符。

该方法的作用是修改对象中索引值为 index 位置的字符为新的字符 ch

4、反转字符串

StringBuffer 类中的 reverse() 方法用于将字符串序列用其反转的形式取代。

5、删除字符串

StringBuffer 类提供了 deleteCharAt() 和 delete() 两个删除字符串的方法,下面详细介绍。

1、deleteCharAt() 方法

deleteCharAt() 方法用于移除序列中指定位置的字符

deleteCharAt() 方法的作用是删除指定位置的字符,然后将剩余的内容形成一个新的字符串。

执行该段代码,将字符串 sb 中索引值为 2 的字符删除,剩余的内容组成一个新的字符串,因此对象 sb 的值为 Sh。

2、delete() 方法

delete() 方法用于移除序列中子字符串的字符

其中,start 表示要删除字符的起始索引值(包括索引值所对应的字符),end 表示要删除字符串的结束索引值(不包括索引值所对应的字符)。该方法的作用是删除指定区域以内的所有字符,

数字和日期处理

六、Math类的常用方法

Java 中的 +、-、*、/ 和 % 等基本算术运算符不能进行更复杂的数学运算,例如,三角函数、对数运算、指数运算等。于是 Java 提供了 Math 工具类来完成这些复杂的运算。

在 Java 中 Math 类封装了常用的数学运算,提供了基本的数学操作,如指数、对数、平方根和三角函数等。Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 Math 类的对象,并且 Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。

1、静态常量

Math 类中包含 E 和 PI 两个静态常量,正如它们名字所暗示的,它们的值分别等于 e(自然对数)和 π(圆周率)。

2、求最大值、最小值

math类中获取最大值和最小值

int a = 56;

    int b = 23;

    System.out.println("最大值为:" + Math.max(a, b));

    System.out.println("最小值:" + Math.min(a, b));

七、生成随机数(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 类中常用的方法。

Math 类的 random() 方法没有参数,它默认会返回大于等于 0.0、小于 1.0 的 double 类型随机数,即 0<=随机数<1.0。对 random() 方法返回的数字稍加处理,即可实现产生任意范围随机数的功能。

由于 m+(int)(Math.random()*n) 语句可以获取 m~m+n 的随机数,所以 2+(int)(Math. random()*(102-2)) 表达式可以求出 2~100 的随机数。在产生这个区间的随机数后还需要判断是否为偶数,这里使用了对 2 取余数,如果余数不是零,说明随机数是奇数,此时将随机数加 1 后再输出。

八、数字格式化

数字的格式在解决实际问题时使用非常普遍,这时可以使用 DedmalFormat 类对结果进行格式化处理。例如,将小数位统一成 2 位,不足 2 位的以 0 补齐。

DecimalFormat 是 NumberFormat 的一个子类,用于格式化十进制数字。DecimalFormat 类包含一个模式和一组符号,常用符号的说明如下表所示。

九、Java大数字运算

在 Java 中提供了用于大数字运算的类,即 java.math.BigInteger 类和 java.math.BigDecimal 类。这两个类用于高精度计算,其中 BigInteger 类是针对整型大数字的处理类,而 BigDecimal 类是针对大小数的处理类。

1、BigInteger 类

如果要存储比 Integer 更大的数字,Integer 数据类型就无能为力了。因此,Java 中提供 BigInteger 类来处理更大的数字。

BigInteger 类型的数字范围较 Integer 类型的数字范围要大得多。BigInteger 支持任意精度的整数,也就是说在运算中 BigInteger 类型可以准确地表示任何大小的整数值。

除了基本的加、减、乘、除操作之外,BigInteger 类还封装了很多操作,像求绝对值、相反数、最大公约数以及判断是否为质数等。

要使用 BigInteger 类,首先要创建一个 BigInteger 对象。BigInteger 类提供了很多种构造方法,其中最直接的一种是参数以字符串形式代表要处理的数字。

注意:这里数字 5 的双引号是必需的,因为 BigInteger 类构造方法要求参数是字符串类型。

创建 BigInteger 对象之后,便可以调用 BigInteger 类提供的方法进行各种数学运算操作,下表列出了 BigInteger 类的常用运算方法。

2、BigDecimal 类

BigInteger 和 BigDecimal 都能实现大数字的运算,不同的是 BigDecimal 加入了小数的概念。一般的 float 和 double 类型数据只能用来做科学计算或工程计算,但由于在商业计算中要求数字精度比较高,所以要用到 BigDecimal 类。BigDecimal 类支持任何精度的浮点数,可以用来精确计算货币值。

BigDecimal 常用的构造方法如下。

1、BigDecimal(double val):实例化时将双精度型转换为 BigDecimal 类型。

2、BigDecimal(String val):实例化时将字符串形式转换为 BigDecimal 类型。

BigDecimal 类的方法可以用来做超大浮点数的运算,像加、减、乘和除等。在所有运算中,除法运算是最复杂的,因为在除不尽的情况下,末位小数的处理方式是需要考虑的。

十、Java时间日期的处理

在 Java 中获取当前时间,可以使用 java.util.Date 类和 java.util.Calendar 类完成。其中,Date 类主要封装了系统的日期和时间的信息,Calendar 类则会根据系统的日历来解释 Date 对象。下面详细介绍这两个类的具体使用。

1、Date 类

Date 类表示系统特定的时间戳,可以精确到毫秒。Date 对象表示时间的默认顺序是星期、月、日、小时、分、秒、年。

1、构造方法

Date 类有如下两个构造方法。

1、Date():此种形式表示分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒),使用该构造方法创建的对象可 以获取本地的当前时间。

2、Date(long date):此种形式表示从 GMT 时间(格林尼治时间)1970 年 1 月 1 日 0 时 0 分 0 秒开始经过参数 date 指定的毫 秒数。

Date 类的无参数构造方法获取的是系统当前的时间,显示的顺序为星期、月、日、小时、分、秒、年。

Date 类带 long 类型参数的构造方法获取的是距离 GMT 指定毫秒数的时间,60000 毫秒是一分钟,而 GMT(格林尼治标准时间)与 CST(中央标准时间)相差 8 小时,也就是说 1970 年 1 月 1 日 00:00:00 GMT 与 1970 年 1 月 1 日 08:00:00 CST 表示的是同一时间。 因此距离 1970 年 1 月 1 日 00:00:00 CST 一分钟的时间为 1970 年 1 月 1 日 00:01:00 CST,即使用 Date 对象表示为 Thu Jan 01 08:01:00 CST 1970。

2、Calendar 类

Calendar 类是一个抽象类,它为特定瞬间与 YEAR、MONTH、DAY_OF—MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(如获得下星期的日期) 提供了一些方法。

创建 Calendar 对象不能使用 new 关键字,因为 Calendar 类是一个抽象类,但是它提供了一个 getInstance() 方法来获得 Calendar类的对象。getInstance() 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化。

当创建了一个 Calendar 对象后,就可以通过 Calendar 对象中的一些方法来处理日期、时间。

Calendar 对象可以调用 set() 方法将日历翻到任何一个时间,当参数 year 取负数时表示公元前。Calendar 对象调用 get() 方法可以获取有关年、月、日等时间信息,参数 field 的有效值由 Calendar 静态常量指定。

Calendar 类中定义了许多常量,分别表示不同的意义。

1、Calendar.YEAR:年份。

2、Calendar.MONTH:月份。

3、Calendar.DATE:日期。

4、Calendar.DAY_OF_MONTH:日期,和上面的字段意义完全相同。

5、Calendar.HOUR:12小时制的小时。

6、Calendar.HOUR_OF_DAY:24 小时制的小时。

7、Calendar.MINUTE:分钟。

8、Calendar.SECOND:秒。

9、Calendar.DAY_OF_WEEK:星期几。

十一、Java日期格式化

1、DateFormat 类

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期→文本)、解析(文本→日期)和标准化日期。

在创建 DateFormat 对象时不能使用 new 关键字,而应该使用 DateFormat 类中的静态方法 getDateInstance()。

在创建了一个 DateFormat 对象后,可以调用该对象中的方法来对日期/时间进行格式化。DateFormat 类中常用方法如下表所示。

格式化样式主要通过 DateFormat 常量设置。将不同的常量传入到表 1 所示的方法中,以控制结果的长度。

2、SimpleDateFormat 类

如果使用 DateFormat 类格式化日期/时间并不能满足要求,那么就需要使用 DateFormat 类的子类——SimpleDateFormat。

SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类,它允许进行格式化(日期→文本)、解析(文本→日期)和规范化。SimpleDateFormat 使得可以选择任何用户定义的日期/时间格式的模式。

SimpleDateFormat 类主要有如下 3 种构造方法。

SimpleDateFormat():用默认的格式和默认的语言环境构造 SimpleDateFormat。

SimpleDateFormat(String pattern):用指定的格式和默认的语言环境构造 SimpleDateF ormat。

SimpleDateFormat(String pattern,Locale locale):用指定的格式和指定的语言环境构造 SimpleDateF ormat。

内置类及包装类

十二、包装类、装箱和拆箱

 1、包装类

在 Java 的设计中提倡一种思想,即一切皆对象。但是从数据类型的划分中,我们知道 Java 中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?于是 Java 为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有地方称为外覆类或数据类型类。

2、装箱和拆箱

在了解包装类后,下面介绍包装类的装箱与拆箱的概念。其实这两个概念本身并不难理解,基本数据类型转换为包装类的过程称为装箱,例如把 int 包装成 Integer 类的对象;包装类变为基本数据类型的过程称为拆箱,例如把 Integer 类的对象重新简化为 int。

手动实例化一个包装类称为手动拆箱装箱。Java 1.5 版本之前必须手动拆箱装箱,之后可以自动拆箱装箱,也就是在进行基本数据类型和对应的包装类转换时,系统将自动进行装箱及拆箱操作,不用在进行手工操作,为开发者提供了更多的方便。

十三、Object类

Object 是 Java 类库中的一个特殊类,也是所有类的父类。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类。因此,以下两个类表示的含义是一样的。

1、toString() 方法

toString() 方法返回该对象的字符串,当程序输出一个对象或者把某个对象和字符串进行连接运算时,系统会自动调用该对象的 toString() 方法返回该对象的字符串表示。

Object 类的 toString() 方法返回“运行时类名@十六进制哈希码”格式的字符串,但很多类都重写了 Object 类的 toString() 方法,用于返回可以表述该对象信息的字符串。

程序中的 Person 类中重写了 Object 类中的 toString() 方法,这样直接输出对象时调用的是被子类重写过的 toString() 方法。

2、equals() 方法

在前面学习字符串比较时,曾经介绍过两种比较方法,分别是`==`运算符和 equals() 方法,`==`运算符是比较两个引用变量是否指向同一个实例,equals() 方法是比较两个对象的内容是否相等,通常字符串的比较只是关心内容是否相等。

3、getClass() 方法

getClass() 方法返回对象所属的类,是一个 Class 对象。通过 Class 对象可以获取该类的各种信息,包括类名、父类以及它所实现接口的名字等。

十五、Integer类

Integer 类在对象中包装了一个基本类型 int 的值。Integer 类对象包含一个 int 类型的字段。此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。

1、Integer 类的构造方法

Integer 类中的构造方法有以下两个:

Integer(int value):构造一个新分配的 Integer 对象,它表示指定的 int 值。

Integer(String s):构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。

2、Integer 类的常量

Integer 类包含以下 4 个常量。

MAX_VALUE:值为 231-1 的常量,它表示 int 类型能够表示的最大值。

MIN_VALUE:值为 -231 的常量,它表示 int 类型能够表示的最小值。

SIZE:用来以二进制补码形式表示 int 值的比特位数。

TYPE:表示基本类型 int 的 Class 实例。

十六、System类

System 类位于 java.lang 包,代表当前 Java 程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类。

System 类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法。

十七、JDK自带记录日志类

每个初学者都很熟悉在有问题的代码中使用 System.out.println 方法在控制台打印消息,来帮助观察程序运行的操作过程。如果你使用  System.out.println 方法,一旦发现问题的根源,就要将这些语句从代码中删去。如果接下来又出现了问题,就需要再插入几个调用 System.out.println 方法的语句,如此反复,增加了工作量。

修改日志管理器配置

可以通过编辑配置文件来修改日志系统的各种属性。在默认情况下,配置文件存在于 jre 安装目录下“jre/lib/logging.properties”。要想使用另一个配置文件,就要将 java.util.logging.config.file 特性设置为配置文件的存储位置,并用下列命令启动应用程序。

第十节

一、Java 中 Lambda 表达式

1、Lambda 简介

Lambda 表达式(Lambda expression)是一个匿名函数,基于数学中的λ演算得名,也可称为闭包(Closure)。现在很多语言都支持 Lambda 表达式,如 C++、C#、Java、 Python 和 JavaScript 等。

Calculable 接口只有一个方法 calculateInt,参数是两个 int 类型,返回值也是 int 类型。

2、Java Lambda 表达式的优缺点

优点:

1. 代码简洁,开发迅速

2. 方便函数式编程

3. 非常容易进行并行计算

4. Java 引入 Lambda,改善了集合操作(引入 Stream API)

缺点:

1. 代码可读性变差

2. 在非并行计算中,很多计算未必有传统的 for 性能要高

3. 不容易进行调试

3、函数式接口

Lambda 表达式实现的接口不是普通的接口,而是函数式接口。如果一个接口中,有且只有一个抽象的方法(Object 类中的方法不包括在内),那这个接口就可以被看做是函数式接口。这种接口只能有一个方法。如果接口中声明多个抽象方法,那么 Lambda 表达式会发生编译错误:

The target type of this expression must be a functional interface

提示:Lambda 表达式是一个匿名方法代码,Java 中的方法必须声明在类或接口中,那么 Lambda 表达式所实现的匿名方法是在函数式接口中声明的。

 二、Lambda表达式的使用

1、作为参数使用Lambda表达式

Lambda 表达式一种常见的用途就是作为参数传递给方法,这需要声明参数的类型声明为函数式接口类型。

2、访问变量

Lambda 表达式可以访问所在外层作用域定义的变量,包括成员变量和局部变量。

访问成员变量

成员变量包括实例成员变量和静态成员变量。在 Lambda 表达式中可以访问这些成员变量,此时的 Lambda 表达式与普通方法一样,可以读取成员变量,也可以修改成员变量。

访问局部变量

对于成员变量的访问 Lambda 表达式与普通方法没有区别,但是访问局部变量时,变量必须是 final 类型的(不可改变)。

3、方法引用

方法引用可以理解为 Lambda 表达式的快捷写法,它比 Lambda 表达式更加的简洁,可读性更高,有很好的重用性。如果实现比较简单,复用的地方又不多,推荐使用 Lambda 表达式,否则应该使用方法引用。

Java 8 之后增加了双冒号`::`运算符,该运算符用于“方法引用”,注意不是调用方法。“方法引用”虽然没有直接使用 Lambda 表达式,但也与 Lambda 表达式有关,与函数式接口有关。 方法引用的语法格式如下:

ObjectRef::methodName

其中,ObjectRef 是类名或者实例名,methodName 是相应的方法名。

注意:被引用方法的参数列表和返回值类型,必须与函数式接口方法参数列表和方法返回值类型一致,

LambdaDemo 类中提供了一个静态方法 add,一个实例方法 sub。这两个方法必须与函数式接口方法参数列表一致,方法返回值类型也要保持一致。

、异常(Exception)处理及常见异常

很多事件并非总是按照人们自己设计意愿顺利发展的,经常出现这样那样的异常情况。例如: 你计划周末郊游,计划从家里出发→到达目的→游泳→烧烤→回家。但天有不测风云,当你准备烧烤时候突然天降大雨,只能终止郊游提前回家。“天降大雨”是一种异常情况,你的计划应该考虑到这样的情况,并且应该有处理这种异常的预案。

计算机程序的编写也需要考虑处理这些异常情况。异常(exception)是在运行程序时产生的一种异常情况,已经成为了衡量一门语言是否成熟的标准之一。目前的主流编程语言,如 C++、c#、Ruby 和 Python 等大都提供了异常处理机制。

1、异常简介

Java 中的异常又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类,这可以让程序具有极好的容错性且更加健壮。

在 Java 中一个异常的产生,主要有如下三种原因:

1、Java 内部错误发生异常,Java 虚拟机产生的异常。

2、编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。

3、通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。

Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。

我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常。

2、异常类型

为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类。在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 Exception 和 Error

Throwable 类是所有异常和错误的超类,下面有 Error 和 Exception 两个子类分别表示错误和异常。其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

1、Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。

2、Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。

这里暂时不讨论关于 Error 类型的异常处理,因为它们通常是灾难性的致命错误,不是程序可以控制的。接下来将讨论 Exception 类型的异常处理。

运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。

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

、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 提供的异常处理机制可以帮用户更好地解决这方面的问题。Java 的异常处理机制可以让程序具有极好的容错性,让程序更加健壮。

Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。

如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。

如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。

注意:try...catch 与 if...else 不一样,try 后面的花括号{ }不可以省略,即使 try 块里只有一行代码,也不可省略这个花括号。与之类似的是,catch 块后的花括号{ }也不可以省略。另外,try 块里声明的变量只是代码块内的局部变量,它只在 try 块内有效,其它地方不能访问该变量。

2、多重catch语句

如果 try 代码块中有很多语句会发生异常,而且发生的异常种类又很多。那么可以在 try 后面跟有多个 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 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。

八、声明和抛出异常

1、throws 声明异常

当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。

其中,returnType 表示返回值类型;method_name 表示方法名;paramList 表示参数列表;Exception 1,Exception2,… 表示异常类。

如果有多个异常类,它们之间用逗号分隔。这些异常类可以是方法中调用了可能拋出异常的方法而产生的异常,也可以是方法体中生成并拋出的异常。

使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。

方法重写时声明抛出异常的限制

使用 throws 声明抛出异常时有一个限制,是方法重写中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。看如下程序。

2、throw 拋出异常

与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,

其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。

throws 关键字和 throw 关键字在使用上的几点区别如下:

1、throws 用来声明一个方法可能抛出的所有异常信息,表示出现异常的一种可能性,但并不一定会发生这些异常;throw 则是指拋出的一个具体的异常类型,执行 throw 则一定抛出了某种异常对象。

通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。

  1. throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。

、自定义异常

如果 Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。

、异常跟踪栈

异常对象的 printStackTrace() 方法用于打印异常的跟踪栈信息,根据 printStackTrace() 方法的输出结果,开发者可以找到异常的源头,并跟踪到异常一路触发的过程。

十一、Collection接口

Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

注意:以上方法完全来自于 Java API 文档,可自行参考 API 文档来查阅这些方法的详细信息。无需硬性记忆这些方法,可以和实际生活结合记忆。集合类就像容器,现实生活中容器的功能,就是添加对象、删除对象、清空容器和判断容器是否为空等,集合类为这些功能都提供了对应的方法。

十二、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 的迭代器返回它们的顺序排列的。

注意:当调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素,也就是说这个方法不会改变 List 集合的长度。

2、LinkedList类

LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。

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 集合中。

2、TreeSet 类

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 的值。

注意:在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 集合中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。

十四、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 类可以对键对象进行排序。

注意:TreeMap 类的使用方法与 HashMap 类相同,唯一不同的是 TreeMap 类可以对键对象进行排序

十五、遍历Map集合

Map 集合的遍历与 List 和 Set 集合不同。Map 有两组值,因此遍历时可以只遍历值的集合,也可以只遍历键的集合,也可以同时遍历。Map 以及实现 Map 的接口类(如 HashMap、TreeMap、LinkedHashMap、Hashtable 等)都可以用以下几种方式遍历。

十六、Collections类

Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。

1、排序(正向和逆向)

Collections 提供了如下方法用于对 List 集合元素进行排序。

- void reverse(List list):对指定 List 集合元素进行逆向排序。

- void shuffle(List list):对 List 集合元素进行随机排序(shuffle 方法模拟了“洗牌”动作)。

- void sort(List list):根据元素的自然顺序对指定 List 集合的元素按升序进行排序。

- void sort(List list, Comparator c):根据指定 Comparator 产生的顺序对 List 集合元素进行排序。

- void swap(List list, int i, int j):将指定 List 集合中的 i 处元素和 j 处元素进行交换。

- void rotate(List list, int distance):当 distance 为正数时,将 list 集合的后 distance 个元素“整体”移到前面;当 distance 为负数时,将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度。

 2、查找、替换操作

Collections 还提供了如下常用的用于查找、替换集合元素的方法。

- int binarySearch(List list, Object key):使用二分搜索法搜索指定的 List 集合,以获得指定对象在 List 集合中的索引。如果要使该方法可以正常工作,则必须保证 List 中的元素已经处于有序状态。

- Object max(Collection coll):根据元素的自然顺序,返回给定集合中的最大元素。

- Object max(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最大元素。

- Object min(Collection coll):根据元素的自然顺序,返回给定集合中的最小元素。

- Object min(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最小元素。

- void fill(List list, Object obj):使用指定元素 obj 替换指定 List 集合中的所有元素。

- int frequency(Collection c, Object o):返回指定集合中指定元素的出现次数。

- int indexOfSubList(List source, List target):返回子 List 对象在父 List 对象中第一次出现的位置索引;如果父 List 中没有出现这样的子 List,则返回 -1。

- int lastIndexOfSubList(List source, List target):返回子 List 对象在父 List 对象中最后一次出现的位置索引;如果父 List 中没有岀现这样的子 List,则返回 -1。

- boolean replaceAll(List list, Object oldVal, Object newVal):使用一个新值 newVal 替换 List 对象的所有旧值 oldVal。

3、复制

Collections 类的 copy() 静态方法用于将指定集合中的所有元素复制到另一个集合中。执行 copy() 方法后,目标集合中每个已复制元素的索引将等同于源集合中该元素的索引。

注意:目标集合的长度至少和源集合的长度相同,如果目标集合的长度更长,则不影响目标集合中的其余元素。如果目标集合长度不够而无法包含整个源集合元素,程序将抛出 IndexOutOfBoundsException 异常。

十七、Lambda表达式遍历Collection集合

Java 8 为 Iterable 接口新增了一个 forEach(Consumer action) 默认方法,该方法所需参数的类型是一个函数式接口,而 Iterable 接口是 Collection 接口的父接口,因此 Collection 集合也可直接调用该方法。

当程序调用 Iterable 的 forEach(Consumer action) 遍历集合元素时,程序会依次将集合元素传给 Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。正因为 Consumer 是函数式接口,因此可以使用 Lambda 表达式来遍历集合元素。

十八、Iterator(迭代器)

Iterator(迭代器)是一个接口,它的作用就是遍历容器的所有元素,也是 Java 集合框架的成员,但它与 Collection 和 Map 系列的集合不一样,Collection 和 Map 系列集合主要用于盛装其他对象,而 Iterator 则主要用于遍历(即迭代访问)Collection 集合中的元素。

Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口。Iterator 接口里定义了如下 4 个方法。

1、boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。

2、Object next():返回集合里的下一个元素。

3、void remove():删除集合里上一次 next 方法返回的元素。

4、void forEachRemaining(Consumer action):这是 Java 8 为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素。

、Lambda表达式遍历Iterator迭代器

Java 8 为 Iterator 引入了一个 forEachRemaining(Consumer action) 默认方法,该方法所需的 Consumer 参数同样也是函数式接口。当程序调用 Iterator 的 forEachRemaining(Consumer action) 遍历集合元素时,程序会依次将集合元素传给 Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值