java se基础1

文章目录

1.什么是java

Java诞生于1995年在sun,在2009年时被Oracle收货。

1.简洁有效:面向对象,简单易学。

2.跨平台性好,java写好的程序可以在任何系统上运行,一次编译处处运行。Java为每一个平台都提供了一个jvm。

3.适合大型企业级管理系统,大型互联网系统(分布式系统)。

2.java语言的分支

Java SE:(标准版)是java基础,早期叫 J2SE,2005改名Java SE(必须)。

Java ME:(移动版)适合移动端的开发,J2ME,2005改名Java ME(不学)。

Java EE: (企业版)面向企业级关系系统的(必学)。

3.java环境 的搭建

1.JRE

JRE:Java Runtime Environment (java运行环境) 不同的计算机系统有不同的JRE,实现了Java的跨平台

JRE内部包含两个部分一个是JVM,一个是Java核心的类库,java程序就是在JVM上运行的。
 
java的核心类库:JRE提供的让程序能运行的java类

2.JDK

JDK: Java Development Kit (Java开发工具)

我们只要安装JDK就可以了。

3.JDK的安装

JDK的版本

jdk1.0(1996年发布)
 
jdk1.1  1.2 1.3 1.4
jdk5.0 (也叫1.5)
jdk6.0
jdk7.0
jdk8.0
 
 
 

安装过程

jdk的安装程序叫jdk-7u72-windows-i586.exe,从中7u72我们知道这个是jdk1.7版本, u72代表update72,对应还有u45,u10等等。

JDK的安装过程中需要注意的几点:

直接双击jdk-7u72-windows-i586.exe(移动硬盘中保存的有)进行安装

目录的选择不要有任何中文,我们在安装开发软件的过程中一定不要有中文!!!(不同版本的jdk不一定兼容)

安装好之后在安装目录会看到jdk1.7和jre7版本,这个jre是在安装过程中下载的。

我当前的安装路径是 D:\Develop\Java

4.环境变量的配置

配置过程

上面这个是jdk目录的的文件夹和文件。

bin目录:存放可执行程序 常用的有javac.exe(java的编译器),java.exe(Java的运行工具,执行工具),jar.exe(打包工具),javadoc.exe(文档生成工具)

db目录: 一个小型的数据库,纯Java实现的。从JDK6.0开始,引入JavaDB,这个数据库很轻便相当于python中的SQLite

include目录:jdk提供的本地接口编程

jre目录:java运行环境,用于运行java程序。

lib目录:java开发包

src.zip: 源码包,src是JDK核心类的代码,比如unit.java等,千万别修改。

特别介绍bin目录下的java.exe和javac.exe

javac.exe是java编译器工具,用于将java文件编译成java字节码文件.class
java.exe是java的运行工具,他会启动jvm,jvm相当于一个虚拟的操作系统,它专门运行Java编译器编译生成的字节码文件

jdk需要使用dos语言命令进行执行java和javac,要使用必须要切换到jdk下的bin中执行,想在任何目录下运行就需要配置环境变量。比如ping命令就配置在环境变量中,在任何位置都可以执行,C:\Windows\System32中就有ping.exe,System32就是配置在环境变量中的系统命令。

打开环境变量,右击我的电脑,属性,高级系统设置

编辑系统变量中的Path,我们发现有个%SystemRoot%\system32, systemroot代表系统的根,指的是C盘的Windows目录

ping指令的执行过程
 
当我们在F盘下ping的时候,首先OS会先在F下来查找ping.exe的文件,如果在F盘找到了ping.exe那就执行,如果没有找到ping.exe,那就去环境变量所指定的路径下去找C\Windows\System32,如果找到了就执行ping.exe,如果找不到就是提示:“不是内部命令”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nt1loziH-1596104376110)(https://ae01.alicdn.com/kf/H122b2f19d4cd49719f54a4f0d5437c45B.jpg)]

在Path路径中配置bin的路径之后就可以在任何目录之下运行,但是像%SystemRoot%\system32一样配置会更加灵活。

单机Path选择新建然后

在系统变量列表中就会多一个JAVA_HOME的变量。

然后在Path中改为

%JAVA_HOME%代替了jdk中bin的目录

查看path,在cmd中使用set path查看

检测是否配置成功

javac

java

java -version

classpath环境变量

  • 当java虚拟机需要运行一个类时,会在classpath环境变量中所定义的路径下寻找所需的class文件
  • 你可以通过set classpath来查看
  • 配置classpath要新建系统变量,然后变量名为classpath,变量值是一个路径例如C:\Users\shining star\Desktop\workset,也可以直接在cmd中使用set classpath=C:\Users\shining star\Desktop\workset(这样是临时创建的,不能一直用,关闭命令行窗口就失效了,所以最好新建系统变量)
  • 如果配置了classpath,我们在运行java程序的时候就会去classpath的路径去找class文件运行。配置了classpath,我们就可以在任意目录来运行我们的java程序,当我们在当前目录下有同样的一个名字的class文件时,依然是先找classpath下的class类,如果classpath下找不到指定的类就在当前文件夹下查找。
  • JDK5.0之后classpath会默认设置为 . 就是当前目录。所以不需要再设置了,只要跳转到class文件所在路径就可以使用java指令进行执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCQNACIq-1596104376117)(https://ae01.alicdn.com/kf/Hb7cdb292f5df4dbabf2f142fa5bc15bd8.jpg)]

path和classpath的区别

  • path是为了在任何地方都能使用java.exe和javac.exe,用于编译java代码。生成字节码文件。
  • classpath是为了在任何地方都能进入到class文件所在位置。从而运行java程序。

4.HelloWorld

写一个HelloWorld!使用文本编辑软件比如EditPlus

class HelloWorld //定义一个类叫HelloWorld,类名要和文件名(HelloWorld.java)一致,并且首字母要大写
{
    public static void main(String[] args) //主函数
    {
        System.out.println("Hello World!");
    }
}
 

编写好java文件之后,cd到指定的目录下,用javac HelloWorld.java将java编译成class字节码文件,然后使用java HelloWorld来将字节码文件执行

javac生成的字节码文件名字其实就是主函数所在类的类名

C:\Users\shining star\Desktop\第一个java程序>java HelloWorld.class
错误: 找不到或无法加载主类 HelloWorld.class
这个是由于java指令使用错误导致的,不需要加.class后缀

5.java的运行机制

  • 我们写的代码都是源代码(.java),源代码不能执行,必须要编译成字节码文件(.class)
  • 当我们运行java HelloWorld的时候在内存中创建JVM,然后把HelloWorld.class加载到JVM中然后再运行,程序执行完毕释放JVM。
编译:javac 文件名.java  注意:c是compile的缩写
运行:java 字节码文件名  注意:运行在JVM上。
 
JVM是java的字节码文件运行时候创建的虚拟机,然后再运行class文件。

6.标识符和关键字

1.命名规则

  • 标识符和关键字中能够出现字母,数字,下划线和$,但是不能以数字开头。
  • java是区分大小写的,HelloWorld和helloworld不是同一个标识符
标识符的规范:
 
类和接口
首字符大写,如果是多个单词,每一个单词的首字符都要大写
XxxYyyZzz
如:PersonDao Iterator ArrayList
 
变量和方法:
第一个单词的首字符小写后续的单词首字符大写
xxxYyyZzz
如 addPerson,驼峰模式
 
常量:
多个单词定义常量时候每个单词都大写,单词之间使用“_”
XXX_YYY_ZZZ
如:PERSON_COUNT
 

2.关键字

被java语言赋予特殊含义的单词。关键字不能再自己使用来赋予含义。

7.注释

  • 多写注释是个好习惯

单行注释//

//这里是注释,最好写在代码的上面
int i = 5;

多行注释/**/

/*
这里是多行注释,一般也写在代码的上面
*/
int i = 5;
/* 一般也用来注释掉不需要使用的代码,比如测试用的代码。
System.out.println("helloworld!");
System.out.println("helloworld2!");
System.out.println("helloworld3!");
*/

文档注释/**

  • 是对类或者方法的说明

  • 语法/** 注释的内容 */

  • 可以使用javadoc命令将文档注释提取出来生成帮助文档

/**
* 这是一个HelloWorld程序
  作者:刘喆
  日期:2020-7-2
*/
class HelloWorld //定义一个类叫HelloWorld
{
    /**
    * 这是主方法
    */
    public static void main(String[] args) //主函数
    {
        System.out.println("Hello World!");
    }
}
 

8.常量和变量

1.一个java程序运行的步骤

  • 1.开辟内存空间(创建jvm)

  • 2.加载数据从.class中

  • 3.CPU提取数据运行

    当数据加载到内存中时,我们按着运行期数据的是否变化把数据分成常量和变量

2.常量

  • 在程序的运行期数据不发生变化的量就是常量。
  • 命名规则
常量:
多个单词定义常量时候每个单词都大写,单词之间使用“_”
XXX_YYY_ZZZ
如: PERSON_COUNT (自定义的常量)
    整数的常量:24小时,365天
    小数:重力系数9.8, 圆周率3.14
    字符: ‘男’,'女'
    布尔数值(小写):true,false
    空常量:null。
 

3.变量

  • 在程序运行的过程中存储可以变化的数据的量(内存的空)就叫做变量。

    class Demo1
    {
        public static void main(String[] args){
            int age = 10;//这就是一个变量
            System.out.println(age);
        }
    }
    

4.数据类型

  • 数据类型分为基本数据类型引用数据类型

基本数据类型(8种)

基本数据类型分为8种
大分为整数类型 浮点类型 字符类型 布尔类型
整数类型分为 字节型byte 短整型short 整型int 长整型long
浮点类型 单精度float 双精度double
字符类型char
布尔类型boolean 1 true  0 false

基本数据类型的数值范围

注意:浮点类型所能表示的范围要比整数类型最大范围大。

5.变量的定义

  • 变量的定义必须要有初始值
/**
* 演示变量的定义
*/
 
class Demo3{
 
    public static void main(String[] args){
        //数据类型    变量名 = 初始值;
        byte    age = 10;
        System.out.println("第一次打印:"+age);
        //短整型的定义
        short personCount = 400;
        System.out.println("打印短整型:"+personCount);
 
        //整型的定义
        int itemCount = 10000;
        System.out.println("打印整型:"+itemCount);
        //定义长整型
        //在末尾加上l代表长整型
        long empTime = 10000000l;
        System.out.println("打印长整型:"+empTime);
 
        /*错误 没有初始值 会报错可能尚未初始化变量a,但是后期我们学习的类的属性的概念(不需要初始化)
        int a;
        System.out.println(a);
        */
 
        //定义一个小数类型的变量,float在定义的时候一定要在初始值后面加f!!!
        float price = 10.5f;
        //输出: 价格:10.5
        System.out.println("价格:"+price);
        //定义双精度小数类型
        //double    totalPrice = 100.34;
        //最好在末尾加个d代表double
        double    totalPrice = 100.34d;
        //输出 总价钱:100.34
        System.out.println("总价钱:"+totalPrice);
 
        //定义字符类型,只能放一个字符
        char gender = ' ';
        //输出 性别:
        System.out.println("性别:"+gender);
 
        //定义布尔数据类型
        boolean isMarry = true;
        //输出 婚否:true
        System.out.println("婚否:"+isMarry);
    }
 
}
 
输出:
第一次打印:10
打印短整型:400
打印整型:10000
打印长整型:10000000
价格:10.5
总价钱:100.34
性别:
婚否:true

6.基本数据类型作为类属性时的初始值

  • 变量定义在函数和方法中时一定要初始化,但是在类中定义的变量可以不必初始化,这时变量作为属性
  • 属性的默认初始值

  • 注意 /u代表Unicode码0000代表的是NUL,也就是空的意思,代表空。

7.数据类型的转换

  • 八种数据类型除了boolean都能进行转换
  • 转换分为自动转换强制转换

自动转换

基本数据类型在计算的时候都会向数值范围大的方向转换

1.整数类型自动转换
  • byte、short、char在做计算的时候会自动的提升数据类型成int
/**
* 整数类型转换
*/
 
class Demo6{
 
    public static void main(String[] args){
 
        byte a = 12;
 
        //错误byte b = a + 1;这样会损失精度 a+1是byte进行了计算,默认成为int类型
        //byte在做运算的时候会自动的提升成int
        int b = a + 1;
        System.out.println(b);
 
        short s = 30;
        //short运算时自动提升成int
        int s1 = s + 2;
        System.out.println(s1);
 
        //定义字符类型
        char c = 'a';
        //char类型的数据在参与运算的时候,每一个char背后都对应这个一个数值类型的ascii码,参与计算的是ascii码
        int d = c + 3;
        //输出的是100 a的ASCII码是97加3是100
        System.out.println(d);
 
        //使用short和byte计算也会提升成int a是12 s是30
        int e = a + s;
        //输出42
        System.out.println(e);
 
        //使用short或者byte和char计算 a是12 c是97
        int f = a + c;
        //输出109
        System.out.println(f);
 
        //int一旦达到最大值就会损失精度,下面g定义为int的最大值
        //二进制 0111 1111 1111 1111 1111 1111 1111 1111
 
        int g = 2147483647;
        //int类型如果不和long进行运算是不能提升成long,会直接损失精度
        long l = g + 1;
        //输出 -2147483648 钟表转一个回到最开始
        System.out.println(l);
 
        //任何的整数类型和long类型做运算都会转换成long类型。
        long l1 = 100;
        long l2 = l1+b;
        System.out.println("l2:"+l2);
        //常量在做运算的时候只要不超出定义的数据类型的范围就不会报错,不会发生类型的自动提升
        byte b1 = 12 + 4;
        System.out.println(b1);
 
    }
}
2.浮点类型自动转换
/**
* 浮点类型自动转换
*/
 
class Demo7{
 
    public static void main(String[] args){
        //定义单精度类型变量
        float f = 12.5f;
        //float类型和常量计算不会有类型提升 这里的1是常量
        float f1 = f + 1;
        System.out.println(f1);
        //float和float计算不会有类型提升
        float f2 = f + f1;
        System.out.println(f2);
 
        byte b = 10;
        short s = 20;
        int i = 30;
        long l = 100;
        //float类型和所有整数类型计算都会转换成float类型
        float f3 = f+l;
        //输出112.5
        System.out.println(f3);
        //定义双精度数据类型
        double d = 120.6;
        //double和常量计算会自动转换成double
        double d1 = d + 1;
        System.out.println(d1);
        //double和float类型计算会自动的转换成double类型
        double d2 = d + f;
        System.out.println(d2);
        //double的数据类型和任何整数计算都会转换成double
        double d3 = d + l;
        System.out.println(d3);
 
    }
}

强制转换

  • 一般指大空间的数据类型向小空间的数据类型去转换
/**
* 强制转换
*/
 
class Demo8{
 
    public static void main(String[] args){
        //定义一个整型变量
        int i = 130;
        //把整型强制转换成short
        short s = (short)i;
        //输出130
        System.out.println(s);
        //把整型强制转换成byte,强制转换有可能损失精度,因为要转换的变量的值可能超过目标类型的最大值。
        byte b = (byte)i;
        //输出-126
        /* 130的二进制是1000 0010,1是符号位,取反加一是1111 1110,就是-126
        */
        System.out.println(b);
 
        long l = 120;
        byte b1 = (byte)l;
        System.out.println(b1);
 
 
        //定义一个小数类型
        float f = 12.7f;
        //小数转换后会舍弃小数位
        //int i1 = (int)f;
        //long i1 = (long)f;
        //short i1 = (short)f;
        byte i1 = (byte)f;
        System.out.println(i1);
 
        //double数据类型转换成float
        double d = 23.8;
        float f1 = (float)d;
        System.out.println(f1);
 
        //字符类型转换成其他类型(除了boolean类型以外都可以转换)
        char c = 'b';
        byte bc = (byte)c;
        //输出98
        System.out.println(bc);
 
        /*
        char类型可以转换成下面的所有类型
        short
        int
        long
        float
        double
        */
        //整型转换成字符型
        int i3 = 36;
        char c1 = (char)i3;
        //输出$
        System.out.println(c1);
 
    }
}
 
输出:
130
-126
120
12
23.8
98
$

ASCII码表

9.运算符

1.算数运算符

  • 算数运算符主要有+ - * / % ++ –
int i4 = 10;
//当后加加和减减和其他代码在一行的时候先使用加加和减减之前的值, 如果不在同一行,后面的一行就会得到加加或减减后的值
//输出10
System.out.println("同一行中后加加:"+(i4++));
//输出11
System.out.println(i4);

2.赋值运算符

  • 赋值运算符有 = += *= /= %=

    /**
     * 赋值运算符
     */
    
    class Demo10{
    
            public static void main(String[] args){
    
            int a = 10;
            //相当于a = a + 5
            a += 5;
            System.out.println(a);
    
            int b = 20;
            //相当于b = b - 10;
            b -= 10;
            System.out.println(b);
    
            int c = 5;
            //相当于c = c*2
            c *= 2;
            System.out.println(c);
    
            int d = 8;
            //相当于d = d/2;
            d /= 2;
            System.out.println(d);
    
            int e = 10;
            //相当于e = e%3;
            e %= 3;
            System.out.println(e);
        }
    }
    
    输出:
    15
    10
    10
    4
    1
    

3.比较运算符

  • 比较运算符 == != < > <= >=

  • 比较运算符对两个数值或变量进行比较,其结果是一个布尔值,true或者false

    int a = 20;
    int b = 20;
    boolean c = (a == b);
    //输出true
    System.out.println(c);
    

4.逻辑运算符

  • 逻辑运算符有 & | ^ ! && ||
/**
* 逻辑运算符
*/
 
class Demo12{
 
 
    public static void main(String[] args){
 
        int java = 80;
        int sql = 19;
        /*判断两门考试都大于60分是及格,只要有一个表达式是false那么整体就是false,
        但是&不管第一个表达式是不是false,所有的表达式会全部执行*/
        //System.out.println((java > 60)&(sql > 60));
        //判断&右边的表达式是否会执行,就对sql进行++自增操作
        //System.out.println((java > 60)&(++sql > 60));
        //输出20,所以&左右都会执行到位
        //System.out.println(sql);
 
        //判断两门课程如果有一门课程或者一门以上及格就算你通过
        //只要有一个表达式是true那么整体就是true,但是|不管第一个表达式是不是true,所有的表达式会全部执行
        //System.out.println((java > 60)|(++sql > 60));
        //System.out.println(sql);
 
        //&&比&更加智能,效率更高,若已经判断某个表达式是false那么就不用再继续判断了
        //System.out.println((java > 60)&&(++sql > 60));
        //System.out.println(sql);
 
        //||比|效率更高,若已经判断某个表达式是true那么就不用再继续判断了
        System.out.println((java > 60)||(++sql > 60));
        System.out.println(sql);
 
        //把结果取反
        System.out.println(!(java>60));
 
        //异或^
        //规则 true^true得false false^false得false false^true得true true^false得true
        //用于连接多个表达式的判断,如果两个表达式相同就是false,不同是true,两边都参加计算
        //输出true, java>60是true,sql>80是false,所以最终是true
        System.out.println((java > 60)^(sql > 80));
 
    }
}

&& 和 & ,||和|有什么区别?

  • &&和||的效率更高,&&也叫短路与,在检测到某个表达式是false时,用&&连接的后面的表达式就不会去判断了,而&却都要判断。||也叫短路或,在检测到某个表达式值是true时,||后面的表达式就不用判断了,而|却都要判断。

5.位运算符

  • 位运算符有&(按位与) |(按位或) ~(取反) ^(按位异或) <<左移 >>右移 >>>无符号右移
/**
* 位运算符
*/
 
class Demo13{
 
 
 
    public static void main(String[] args){
        int a = 2;
        //把a变量的二进制的值左移2位
        //面试题:如何把2用最高的效率变成8,答案是左移2位
        System.out.println(a << 2);
 
        //3是0000 0011
        int b = 3;
        System.out.println(b >> 3);
 
        //-3原码1000 0011 反码1111 1100 补码1111 1101
        //右移两位高位补1成为1111 1111(补码,它的原码是-1)了,计算机操作的都是补码
        System.out.println(-3 >> 2);
 
        int c = -3;
        //正数无符号右移 0000 0011无符号右移3位0000 0000就是0
        System.out.println(b >>> 3);
        //负数无符号右移
        //-3是int类型,它的二进制是‭1111 1111 1111 1111 1111 1111 1111 1101‬(-3的补码)
        //无符号右移3位是0001 1111 1111 1111 1111 1111 1111 1111 就是536870911
        System.out.println(c >>> 3);
 
    }
}
输出:
8
0
-1
0
536870911
  • 记住:无符号右移不考虑符号位全部补0,0右移正数补0,右移负数补1

10.程序流程控制

  • 主要分为三大结构 顺序结构 选择结构循环结构

1.if需要注意的一点

  • if里面的结果一定是boolean类型的值 这一点和C语言的语法不一样,C语言的if里面不是0和NULL就是1
public static void main(String[] args){
        int grade = 2;
 
        /*
            if里面的结果一定是boolean类型的值 这一点和C语言的语法不一样,C语言的if里面不是0和NULL就是1
        //错误写法:
        if(grade){
 
        }
 
        Demo5.java:29: 错误: 不兼容的类型
                if(grade)
                   ^
        需要: boolean
        找到:    int
        1 个错误
        */
 
 
        boolean isTrue = true;
        if(isTrue){
            System.out.println("正确");
        }

2.分支判断switch…case…default

  • if一般用于区间判断,括号里面的表达式必须是boolean类型,而switch用于等值判断,括号里面的表达式可以是任意类型

    class Demo6{
    
        public static void main(String[] args){
            int grade = 2;
            /*括号中的表达式可以是任意类型,这和java的if规则不一样,if括号里面必须是boolean
              表达式可以是byte short int char,
              Jdk1.7可以用String
            */
            switch(grade){
                //case 常量,这个常量要和表达式的结果做等值判断,如果判断相等就执行它下面的句子。
                case 1:
                    System.out.println("☆");
                    //跳出switch, 如果没有break,匹配上一个case后就会继续向下执行,直到遇到一个break,所有多数情况我们都要加break
                    break;
                case 2:
                    System.out.println("☆☆");
                    break;
                case 3:
                    System.out.println("☆☆☆");
                    break;
                case 4:
                    System.out.println("☆☆☆☆");
                    break;
                case 5:
                    System.out.println("☆☆☆☆☆");
                    break;
                default:
                    System.out.println("发生错误");
                    break;
            }
        }
    }
    

判断某年某月有多少天

class Demo7{
 
 
    /**
        计算指定的年月份的天数。使用case的穿透执行
     */
    public static void main(String[] args){
        /**
         * 31天的    1, 3, 5, 7, 8, 10 ,12
           30天的         4, 6,9,11
           28天(平年)                2
           29天(闰年)                2
         */
        //年
        int year = 2100;
        //月
        int month = 20;
        //天数
        int days = 0;
 
        //穿透执行
        switch(month){
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                days = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                days = 30;
                break;
            case 2:
            //如果年能被400整除闰年  或者如果能被4整除但是不能被100整除也是闰年
            if(year%400 == 0 || (year%4 == 0 && year%100 != 0)){
                days = 29;
            }else{
                days = 28;
            }
            break;
            //相当于else
            default:
                System.out.println("请确认月份的正确性");
                break;
 
        }
 
        //判断月份的合法性
        if(days != 0){
            System.out.println(year +"年"+month+"月有"+days+"天");
        }
    }
}

3.switch括号内表达式支持的类型

  • switch的括号里面支持表达式可以是byte short int char, Jdk1.7可以用String,不支持long float double

    //必须JDK7.0及以上
    String grade = "aa";
    switch(grade){
            //case 常量,这个常量要和表达式的结果做等值判断,如果判断相等就执行它下面的句子。
        case "aa":
            System.out.println("☆");
            break;
        case "bb":
            System.out.println("☆☆");
            break;
        case "cc":
            System.out.println("☆☆☆");
            break;
        case "dd":
            System.out.println("☆☆☆☆");
            break;
        case "ee":
            System.out.println("☆☆☆☆☆");
            break;
        default:
            System.out.println("发生错误");
            break;
    }
    

4.三目运算符

int a = 1;
int b = 2;
int c = a>b?1:2;

5.打印乘法口诀表

class Demo9{
 
    public static void main(String[] args){
        int i = 1,j = 1;
        int value = 0;
        for(;i<=9;++i)
        {
            j = 1;
            for(;j<=i;++j)
            {
                value = i*j;
                System.out.print(i+"*"+j+"="+value+" ");
            }
            System.out.println();
        }
    }
}
输出:
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81

11.函数和数组

1.函数

格式
<public> <static> 返回值类型[void] 方法名([数据类型 参数名, 数据类型 参数名,..]){
    //方法体
    [return 结果]
}
 
方法名运用驼峰命名法 第一个单词首字母小写,后面的首字母都大写。
返回值有int float double char ....如果返回值类型是void那么就不需要returnstatic关键字修饰的函数存在于内存中的静态方法区。
 
通过值传递的方式把实参的值传递给方法的形参
Java语言中只有值传递,因为Java中没有指针。只有引用。

2.方法的重载

重载(overload):在一个类中有两个或者两个以上同名的方法,但是参数不同(两个方法的参数的个数不同或者参数的类型不同),跟返回值无关

重载的目的:在提高代码的可读性和节省命名的词。

class Demo{
    //一个类中有两个方法的名字相同,但是参数类型或者参数个数不同。就是方法的重载
    /**
      比较两个数值的大小
     */
    public static int compareNum(int a, int b){
        if(a > b){
            return a;
        }else{
            return b;
        }
    }
 
    /**
     *比较三个数值的大小
     */
    public static int compareNum(int a, int b, int c){
        //比较a和b,直接可以调用两个参数比较的方法
        int resultab = compareNum(a, b);
        //比较a和b中大的数和c的大小
        int resultabc = compareNum(resultab, c);
        return resultabc;
    }
}  
 

3.数组

  • java的数据类型

  • 引用数据类型有3种,数组,类,接口

数组的定义

  • 数组就是一组数据,一个数据的集合。

语法:

数据类型[] 变量名 = new 数据类型[整数];

整数:数组的长度

class Demo7{
 
    public static void main(String[] args){
        //数据类型[] 变量名 = new 数据类型[整数];
        int[] array = new int[7];   
        //给数组赋值
        array[0] = 34;
        array[1] = 23;
        array[2] = 4;
        array[3] = 8;
        array[4] = 87;
        array[5] = 65;
        array[6] = 43;
 
        //获得数组的长度
        System.out.println(array.length);
 
        //打印数组的值
 
        /*
        for(int i = 0; i < 7; i++){
            System.out.print(array[i] + "\t");
        }
        */
        /*
         for(int i = 0; i < array.length; i++){
            System.out.print(array[i] + "\t");
        }
        */
        //调用打印数组的方法
        printArray(array);
    }
 
    public static void printArray(int[] array){
        for(int i = 0; i < array.length; i++){
            System.out.print(array[i] + "\t");
        }
 
    }
 
 
}
  • 栈:空间小,主要存储地址和基本类型的变量,存取速度快

  • 堆:空间大,主要存储引用类型的变量,存取速度慢,堆适合存储对象

  • jvm对数组的存储如下图所示:

判断数组的长度

int[] array = new int[5];
//数组对象都有一个属性叫做length
System.out.println(array.length);

数组元素的赋值(三种方式)

方法一:
int[] array = new int[3];   
//给数组赋值
array[0] = 34;
array[1] = 23;
array[2] = 4;
 
方法二:
int[] array = {34,23,4};
 
方法三:
int[] array = new int[]{34,23,4};
char[] arr = new char[]{'a','b','c'};

数组元素的默认值

  • byte short int long 默认是0
  • float和double默认是0.0
  • char默认是’\u0000’
  • boolean默认是false
  • 引用数据类型的默认值是null,表示变量不引用任何对象。

数组下标越界异常

  • ArrayIndexOutOfBoundException数组下标越界异常

数组的遍历

public static void printArray(int[] array){
        for(int i = 0; i < array.length; i++){
            System.out.print(array[i] + "\t");
        }      
}

冒泡排序

//要求从小到大排序
public static void bubble(int[] arr){
    //如果一轮没有发生任何交换那么就直接结束,这样效率高一些
    int flag;
    int i=0,j=0;
    int temp;
 
    for(i=0;i<arr.length;++i){
        flag = 0;
        for(j=1;j<arr.length-i;++j)
        {
            if(arr[j-1] > arr[j]){
                flag = 1;
                temp = arr[j-1];
                arr[j-1] = arr[j];
                arr[j] = temp;
            }
        }
        if(flag==0)
        {
            return;
        }
    }
}

二维数组

数组中还有数组

数据类型[][] 数组名 = new 数据类型[整数] []

二维数组的第一维必须指定。

class Demo18{
 
 
    public static void main(String[] args){
 
        //int[][] array = new int[3][4];
        /* 方法一:
        array[0][0] = 12;
        array[0][1] = 23;
        array[0][2] = 76;
        array[0][3] = 98;
 
        array[1][0] = 45;
        array[1][1] = 11;
        array[1][2] = 55;
        array[1][3] = 11;
 
        array[2][0] = 57;
        array[2][1] = 1;
        array[2][2] = 4;
        array[2][3] = 7;
        */
 
        //方法二:
        int[][] array = {{1,2,3,5},{1,5,61,2},{12,34,5,6}};
 
        //方法三:
        //int[][] array = new int[][]{{1,2,3,5},{1,5,61,2},{12,34,5,6}};
 
        printArray(array);
 
    }
 
    public static void printArray(int [][] array){
        for(int i = 0; i < 3; i++){
            for(int j = 0; j < 4; j++){
                System.out.print(array[i][j]+"\t");
            }
            //换行
            System.out.println();
        }
    }
 
 
}

12.面向对象

1.理解面向对象

要理解什么是面向对象首先需要了解面向过程

面向过程:POP (procedure oriented programming )就是把解决问题的步骤用函数一步一步实现

面向对象:OOP(Object Oriented Programming)在程序中使用对象来映射现实的事物。使用对象的关系来描述事物之间的联系。

类:具有相同属性和行为的事物可以抽象成一个类。

在java中除了基本数据类型以外都是对象。比如数组 类 和接口

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

2.类

  • 我们把某些具有相同的属性和行为的事物抽象成一类。
  • 类的属性可以不赋初始值。

狗类:

属性:毛发颜色,眼睛颜色,重量,年龄

行为:看家,睡觉,摇尾巴

对象:一个叫小旺的狗就是对象,也就是一个具体的实例,是狗类的实例

class Dog{
    //成员变量会自动赋初始值
    //名字
    String name;
    //颜色
    String color;
    //重量
    double weight;
    //身高
    double height;
 
    //行为(方法)
    //方法是可以使用自己的属性的
    public void protectHome(){
        System.out.println(name+"正在看家");
    }
 
    public void sleep(){
        System.out.println(name+"正在睡觉");
    }
 
}
 
/**
* 测试类
*/
class TestDog{
 
    public static void main(String[] args){
        //类名(数据类型) 变量名 = new 类名();
        //根据Dog类创建了一个具体的对象
        Dog dog = new Dog();
        //对象的变量.属性名 = 具体值;
        dog.name = "栗子黄";
        dog.color = "黄色";
        dog.weight = 20.0;
        dog.height = 0.5;
        System.out.println("-----------dog的第一次打印-------------------------");
        System.out.println("狗的颜色:"+dog.color);
        System.out.println("狗的体重:"+dog.weight);
        System.out.println("狗的高度:"+dog.height);
        System.out.println("-----------dog1的第一次打印-------------------------");
        //又创建了一个狗
        Dog dog1 = new Dog();
        dog1.name = "虎子";
        dog1.color = "黑色";
        dog1.weight = 10.0;
        dog1.height = 0.3;
        System.out.println("狗的颜色:"+dog1.color);
        System.out.println("狗的体重:"+dog1.weight);
        System.out.println("狗的高度:"+dog1.height);
        System.out.println("------------dog2的第一次打印------------------------");
        //定义一个dog2,把dog1赋值给dog2,显然dog1和dog2指向的是同一个堆内存
        Dog dog2 = dog1;
        dog2.color = "白色";
 
        System.out.println("狗的颜色:"+dog2.color);
        System.out.println("狗的体重:"+dog2.weight);
        System.out.println("狗的高度:"+dog2.height);
 
        System.out.println("--------------dog1的第二次打印----------------------");
        System.out.println("狗的颜色:"+dog1.color);
        System.out.println("狗的体重:"+dog1.weight);
        System.out.println("狗的高度:"+dog1.height);
 
 
        //类的对象的方法
        //对象的变量名字.方法名([参数]);
        dog.protectHome();
        dog1.protectHome();
        dog2.protectHome();
        dog.sleep();
 
    }
 
}
-----------dog的第一次打印-------------------------
狗的颜色:黄色
狗的体重:20.0
狗的高度:0.5
-----------dog1的第一次打印-------------------------
狗的颜色:黑色
狗的体重:10.0
狗的高度:0.3
------------dog2的第一次打印------------------------
狗的颜色:白色
狗的体重:10.0
狗的高度:0.3
--------------dog1的第二次打印----------------------
狗的颜色:白色
狗的体重:10.0
狗的高度:0.3
栗子黄正在看家
虎子正在看家
虎子正在看家
栗子黄正在睡觉
 
  • 上述代码编译之后会产生两个字节码文件Dog.class 和 TestDog.class,但是只能运行TestDog.class

  • 在内存中的情况是

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R9GSM9E6-1596104376165)(https://ae01.alicdn.com/kf/H7e9ab8fc48954898928e0990de9527c4r.jpg)]

  • 在程序执行完毕之后,栈内存的会出栈,堆内存保存的会通过gc回收,至于什么时候回收不知道。

3.成员变量的默认初始值

byte short int都是0

long是0L

float是0.0f

double是0.0d

char是空字符,’\u0000’

boolean是false

引用数据类型是null

4.类的命名格式

  • 类的格式所有的单词首字母都要大写

5.局部变量和成员变量的区别

局部变量:

 1.在方法中或者在语句块中如 static{

                语句块;//局部变量

    }

 2.局部变量必须赋初始值才能用;

 3.都存储在栈中(比如new出来的对象的对象名就是局部变量,存储在栈中,栈保存的对象的地址)

 4.生命周期:定义的时候产生,当前的方法的括号语句块的括号执行完毕就会消失。

成员变量(属性)

 1.在类里面定义

 2.可以不赋值,如果不赋值都有默认值。

 3.存储在堆中

 4.生命周期:随着对象的消失而消失,会被jvm的垃圾回收器来回收掉

6.匿名对象(无意义的,没有引用)

class Cat{
 
    String name;
    //属性
    //颜色
    String color;
 
    //重量
    double weight;
 
    //身高
    double height;
 
    //行为(方法)
    //方法是可以使用自己的属性的
    public void eat(){
        System.out.println(name+"正在吃鱼");
    }
 
    public void sleep(){
        System.out.println(name+"正在睡觉");
    }
 
}
 
/**
* 测试类
*/
class TestCat{
    public static void main(String[] args){
        //创建匿名对象
        new Cat().name = "咪咪";
        new Cat().color = "白色";
 
    }
 
}
 
  • 匿名对象是无意义的,因为在栈中没有一个变量对它做引用,很快就会被垃圾回收器gc回收掉。

    实际开发中要避免匿名对象

7.封装private将属性私有化

  • 将属性私有化,使用private关键字完成属性私有化,这种属性只有本类能访问
  • 目的:提高数据安全性,通过封装,可以实现对属性的访问权限控制,同时增加了程序的可维护性。
  • 如果在其他类中直接访问一个带有private 的属性时报错
class Car{
    //将属性私有化,并提供一个public方法用来给对象的属性赋值
    private String color;
 
    private int carNo;
 
    private String name;
 
    public void setAttribute(String color, int carNo, String name){
        this.color = color;
        this.carNo = carNo;
        this.name = name;
    }
    public void run(){
        System.out.println("run--------"+this.color);
    }
 
 
    public void introduce(){
        System.out.println("我是一辆"+this.color+"车牌是"+this.carNo+this.name+"车");
    }
 
}
 
class TestCar{
 
    public static void main(String[] args){
        //创建一个car
        Car car = new Car();
        car.setAttribute("红色", 1, "Toyota");
 
        //调用run方法
        car.run();
        car.introduce();
    }
 
}
输出:
run--------红色
我是一辆红色车牌是1Toyota车

8.this关键字

  • public的权限使得所有类都可以访问
  • private只允许本类才能访问
  • 首先我们来看一个例子
class Car{
    //车的颜色
    String color;
    //车的号牌
    int carNo;
    //车的名字
    String name;
    //启动
    public void run(){
        System.out.println("run--------"+this);
    }
 
    //车介绍
    public void introduce(){
        System.out.println("我是一辆"+color+"车牌是"+carNo+name+"车");
    }
 
}
 
class TestCar{
 
    public static void main(String[] args){
        //创建一个car,在堆中开辟一个空间,栈中有一个引用car指向堆中的对象
        Car car = new Car();
 
        //打印对象
        System.out.println("main--------"+car);
 
        //调用run方法
        car.run();
    }
 
}
 
输出
main--------Car@bcda2d
run--------Car@bcda2d
 
  • Car@bcda2d是一个地址

  • 打印一个对象,输出是这个对象的地址(jDK提供的一个地址)

  • 上述的this不加默认有一个this,只是为了区分属性和参数,this后面一定是属性。

  • this只能在类的对象方法中使用.

    this代表当前调用这个this所在的方法的对象的自身

    this可以在方法内区分同名的类的属性和参数名有this的一定是属性,没有this的一定是方法的参数名。

程序执行原理:

第一步:java TestCar时,把TestCar.class载入到jvm,把main放入方法区,把main方法压栈。

第二步:加载Car.class进入jvm,然后把run方法和introduce方法加载到方法区,在堆中开辟一个空间创建了一个Car对象,在栈中产生一个变量car指向堆中的对象。然后给car对象三个属性赋值。

第三步:car的run方法进栈,把car变量所指向的地址赋值给this关键字,执行方法体。

第四步:run方法出栈,run中的this消失

第五步:car变量消失,main出栈后消失

第六步:由于Car堆中的对象失去了变量的引用变成了匿名对象,所以也被回收。

9.构造器

构造器的语法:

public 类名(数据类型 参数名,….){

}

目的:创建对象

构造器的特点:

    1.**方法名和类名一致**

    2.**方法没有返回值没有void**

    3.**参数可有可无**

构造器的创建

class Person{
 
    String name;
 
    int age;
 
    char gender;
 
    //在自定义有参数构造器之后,要是还想要使用默认的无参构器需要自己写
    public Person(){
 
    }
    public Person(String name){
        System.out.println("一个人被创建");
        this.name = name;
    }
 
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
 
    public Person(String name, int age, char gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
 
    public void introduce(){
        System.out.println("我是"+name+"     年龄:"+age+"     性别:"+gender);
    }
 
}
 
class TestPerson{
 
 
    public static void main(String[] args){
 
        Person p = new Person("王九州");
        System.out.println(p.name);
        System.out.println(p.age);
        System.out.println(p.gender);
 
        System.out.println("------------------------");
        Person p1 = new Person("陈丽", 20);
        System.out.println(p1.name);
        System.out.println(p1.age);
        System.out.println(p1.gender);
 
        System.out.println("------------------------");
        Person p2 = new Person("李武", 18,'男');
        System.out.println(p2.name);
        System.out.println(p2.age);
        System.out.println(p2.gender);
 
        //如果要自定义构造器,在使用无参构造器时必须重新写一个无参数构造器
        Person p3 = new Person();
        System.out.println("------------------------");
        System.out.println(p3.name);
        System.out.println(p3.age);
        System.out.println(p3.gender);
    }
}
输出
王九州
0
 
------------------------
陈丽
20
 
------------------------
李武
18------------------------
null
0
 
 
  • 我们发现不同的构造器中有很多耦合的地方,比如下面三句经常使用,如何来简化构造器呢?

        this.name = name;
          this.age = age;
          this.gender = gender;
    
  • 可以采用下面的方法

class Person{
 
    String name;
    int age;
    char gender;
    public Person(){
 
    }
 
    public Person(String name){
        this.name = name;
    }
 
    public Person(String name, int age){
        //调用一个参数的构造器,相当于执行了new Person(String name),这样使用的话this必须在第一行
        this(name);
        this.age = age;
    }
 
    public Person(String name, int age, char gender){
        //调用两个参数的构造器 相当于执行了new Person(String name, int age),这样使用的话this必须在第一行
        this(name, age);
        this.gender = gender;
    }
 
    public void introduce(){
        //这里省略了this
        System.out.println("我是"+name+"     年龄:"+age+"     性别:"+gender);
    }
 
}
 
class TestPerson{
 
 
    public static void main(String[] args){
 
        Person p = new Person("王九州");
        System.out.println(p.name);
        System.out.println(p.age);
        System.out.println(p.gender);
 
        System.out.println("------------------------");
        Person p1 = new Person("陈丽", 20);
        System.out.println(p1.name);
        System.out.println(p1.age);
        System.out.println(p1.gender);
 
        System.out.println("------------------------");
        Person p2 = new Person("李武", 18,'男');
        System.out.println(p2.name);
        System.out.println(p2.age);
        System.out.println(p2.gender);
 
        //如果要自定义构造器,在使用无参构造器时必须重新写一个无参数构造器
        Person p3 = new Person();
        System.out.println("------------------------");
        System.out.println(p3.name);
        System.out.println(p3.age);
        System.out.println(p3.gender);
        in
    }
}
 

常见错误1

  • 自定义构造器之后,默认构造器就会失效,如果你在new的时候没有按照构造器的语法就会出错

  • 注意:如果类中没有带有参数的构造器,就可以使用一个隐藏的默认构造器来创建对象,如果一旦有带有参数的构造器,默认的构造器就会被覆盖。我们可以显式的定义出来默认构造器就可以使用了

常见错误2

  • 通过this(实参列表)可以调用本类中的构造器,必须注意,this必须是在方法中的第一行

10.static关键字

  • static含义是静态

修饰在属性上叫类属性

定义类属性:

 static 数据类型 变量名;

类属性访问:

 **类名.类属性名(推荐使用)**

 对象名.类熟悉名(不推荐使用)
class User{
 
    //对象属性
    String username;
    String password;
 
    /**
     * 类属性 static 数据类型 变量名;
     */
    //用户数量
    static int userCount;
 
    //对象方法
    public void introduce(){
        System.out.println("用户名:"+username+"    密码:"+password);
    }
}
 
class TestUser{
    /**
         类:     模板
         对象:    样本
 
     */
    public static void main(String [] args){
        //创建一个user对象
        User user = new User();
        //把类属性做加一,类属性的访问方式:类名.类属性
        User.userCount++;
        //再次创建user对象
        User user1 = new User();
        User.userCount++;
 
        User user2 = new User();
        //使用对象来调用类属性(不建议这么用)
        user2.userCount++;
 
        //输出 人数:3
        System.out.println("人数:"+User.userCount);
 
 
    }
}
  • static修饰在属性上的内存结构如下

  • 类属性和对象属性的生命周期

类属性的生命周期:

 User.class加载到jvm中时类属性就产生了。

 Jvm消失时候类属性就消失了。

对象属性:

 当对象被创建的时候就产生了。

 当对象所在的方法(上面程序是main)执行完毕后对象变为匿名对象,然后对象和对象的属性就会被垃圾回收器回收掉。

修饰在方法上叫类方法

语法:

public static 返回值类型[void] 方法名(数据类型 参数名,….){

 方法体

 return 结果;

}

类方法的调用

 **类名.类方法名(推荐使用)**

 对象名.类方法名(不建议使用)

注意:

在类方法中不能使用对象的属性。

类方法只能使用类属性

类方法的用途:工具类

class Teacher{
 
    String name;
 
    int age;
 
    String lession;
 
    static int teacherCount;
 
    /**
     * 对象的方法,每一个老师的实例可以使用
     */
    public void teach(){
        System.out.println(this.name+"老师在教课");
    }
 
    /*
     * 类方法 不能使用this,this只能用在对象方法中。类方法中只能使用类属性
     */
    public static void sleep(){
        System.out.println(teacherCount+"个老师在睡觉");
    }
 
}
 
class TestTeacher{
 
    public static void main(String[] args){
        /*
        //使用类名来调用类方法
        Teacher.sleep();
        //使用对象来调用类方法
        Teacher t = new Teacher();
        t.sleep();
        */
        Teacher t = new Teacher();
        Teacher.teacherCount++;
        t.name = "任亮";
        //使用类方法
        Teacher.sleep();
    }
 
}

类方法的用途——工具类

  • 在以后的工作中会有大量的工具类,它里面的方法大多都是静态方法,也就是类方法,可以直接使用类名调用即可。
class ArrayUtils{
 
 
    public static void sort(int[] arr){
        for(int i = 0; i < arr.length; i++){
            for(int j = 0; j < arr.length -1 -i; j++){
 
                if(arr[j+1] > arr[j]){
                    int temp =  arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }
 
    public static void printArr(int[] arr){
        for(int i = 0; i < arr.length; i++){
            System.out.print(arr[i]+"\t");
        }
    }
 
 
}
 
class A{
    public static void main(String[] args){
        int [] arr = {2,3,4,5,6,7,8,9,0};
        /* 如果不是类方法,只是对象方法没有static修饰的的话就必须这样使用
        ArrayUtils au = new ArrayUtils();
        au.sort(arr);
        au.printArr(arr);
        */
 
        //类方法直接使用类名调用即可,使用方便
        //排序
        ArrayUtils.sort(arr);
        //打印
        ArrayUtils.printArr(arr);
 
    }
 
}
 

11.单例模式Singleton

  • singleton 单身汉 单独

  • 设计模式一共有23种,是有经验的人总结的设计代码的思想。

  • 单例模式比如阿里巴巴只有一个马云,这个马云就是一个单例。这个单例是属于这个类的,只初始化一次。

  • 将类的构造方法私有化,并创建一个静态方法去返回这个类的实例。

  • 单例模式分为饿汉式和懒汉式。

  • 饿汉式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。

    懒汉式:在类加载时不初始化,等到第一次被使用时才初始化。

  • 懒汉式和饿汉式的安全和性能区别

    (1) 线程安全:饿汉式在线程还没出现之前就已经实例化了,所以饿汉式一定是线程安全的。懒汉式加载是在使用时才会去new 实例的,那么你去new的时候是一个动态的过程,是放到方法中实现的,比如:

    public static synchronized Singleton getInstance(){
            if(instance == null){
       //什么时候用就什么时候new
                instance = new Singleton();
            }
            return instance;
    }
    

    如果这个时候有多个线程访问这个实例,这个时候实例还不存在,还在new,就会进入到方法中,有多少线程就会new出多少个实例。一个方法只能return一个实例,那最终return出哪个呢?是不是会覆盖很多new的实例?这种情况当然也可以解决,那就是加同步锁,避免这种情况发生 。

    (2)执行效率:饿汉式没有加任何的锁,因此执行效率比较高。懒汉式一般使用都会加同步锁,效率比饿汉式差。*
    *(3)内存使用:饿汉式在一开始类加载的时候就实例化,无论使用与否,都会实例化,所以会占据空间,浪费内存。懒汉式什么时候用就什么时候实例化,不浪费内存。

饿汉式(不需要同步)

public class Singleton {
 //一开始类加载的时候就实例化,创建单实例对象
    private static Singleton instance = new Singleton();
    private Singleton(){
 
    }
    public static Singleton getInstance(){
        return instance;
    }
}

懒汉式(要使用同步)

public class Singleton {
 //默认不会实例化,什么时候用就什么时候new
    private static Singleton instance = null;
    private Singleton(){
 
    }
    public static synchronized Singleton getInstance(){
        if(instance == null){
   //什么时候用就什么时候new
            instance = new Singleton();
        }
        return instance;
    }
}

采用饿汉式不需要同步,更推荐使用

class Singleton{
    /*饿汉式 推荐
    private static Singleton instance = new Singleton();
    //将构造方法私有化
    private Singleton(){
 
    }
    //用一个静态方法返回这个类的实例,不能是对象方法因为构造器私有化了。
    public static Singleton getInstance(){
        return instance;
    }
    */
 
    //懒汉式 不推荐
    private static Singleton instance = null;
    //将构造方法私有化
    private Singleton(){
 
    }
    //用一个静态方法返回这个类的实例,不能是对象方法因为构造器私有化了。
    public static synchronized Singleton getInstance(){
        if(instance == null)
        {
            instance = new Singleton();
        }
        return instance;
    }
}
 
 
class TestSingle{
    public static void main(String [] args){
        Singleton s = Singleton.getInstance();
 
        Singleton s1 = Singleton.getInstance();
 
        Singleton s2 = Singleton.getInstance();
        System.out.println(s);
        System.out.println(s1);
        System.out.println(s2);
 
    }
 
}
输出:
Singleton@3ef810
Singleton@3ef810
Singleton@3ef810
上述代码只有一个线程调用getInstance时候已经有了同步关系,所以即使忘记加synchronized也没事,但是要是同时有多个线程调用getInstance方法时候必须使用synchronized来进行同步。
  • 直接打印对象输调用Object 类提供的toString方法总是返回该对象实现类的类名 + @ +hashCode值
  • JVM内存分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hrdc64m6-1596104376197)(https://ae01.alicdn.com/kf/H73bbd19f0a5848f7b744b30b3fa38c8cT.jpg)]

13.类的继承

  • 关键字extends

    语法:

    class 子类 extends 父类{

     子类的属性
    
     子类的方法
    

    }

    继承的特点:

    1.如果父类中包含了某些类中的共同的属性和行为,我们可以使用继承来设计程序。

    2.子类使用extends关键字继承父类的共同属性以外,子类还可以有自己特有的属性或者方法。

    3.父类更通用,子类更具体。

    4.子类只能获得父类中的非private的属性,如果想要继承就得提供公共的set和get的方法。 私有的方法 是无法继承下来的

    5.java中只能做单继承,C++可以多继承,但是java可以多层继承。

1.继承的使用(父类的属性不私有化public)

/**
* 父类
*/
class Teacher{
    //姓名
    String name;
    //年龄
    int age;
    //休息方法
    public void sleep(){
        System.out.println("老师在睡觉");
    }
 
}
/**
*
* 子类的定义
*/
//class 子类 extends 父类{
class JavaTeacher extends Teacher{
 
    public void teachJava(){
 
        System.out.println(name+"老师正在教java"+"   年龄:"+age);
    }
 
 
}
 
class PHPTeacher extends Teacher{
 
    public void teachPHP(){
        System.out.println(name+"老师在教PHP"+"   年龄:"+age);
    }
}
 
class ExtendsDemo2{
    public static void main(String[] args){
        JavaTeacher  jt = new JavaTeacher();
        //子类继承了父类的方法sleep
        jt.sleep();
        jt.name = "张三";
        jt.age = 30;
        jt.teachJava();
 
        PHPTeacher pt = new PHPTeacher();
        pt.sleep();
        pt.name = "李四";
        pt.age = 40;
        pt.teachPHP();
    }
 
}
输出:
老师在睡觉
张三老师正在教java   年龄:30
老师在睡觉
李四老师在教PHP   年龄:40

2.继承的使用(父类的属性封装私有化private)

/**
* 父类
*/
class Teacher{
    //姓名
    private String name;
    //年龄
    private int age;
    //休息方法 在类中可以直接使用private修饰的属性
    public void sleep(){
        System.out.println(this.name+"老师在睡觉");
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
 
}
/**
*
* 子类的定义
*/
//class 子类 extends 父类{
class JavaTeacher extends Teacher{
 
    public void teachJava(){
        //这里getName()不能写成name或者this.name因为name属性是private修饰的,不能再别的类中直接访问。这里getName()前面影藏了this,可以写成this.getName()
        System.out.println(getName()+"老师正在教java"+"   年龄:"+getAge());
    }
 
 
}
 
class PHPTeacher extends Teacher{
 
    public void teachPHP(){
        System.out.println(getName()+"老师在教PHP"+"   年龄:"+getAge());
    }
}
 
class ExtendsDemo2{
    public static void main(String[] args){
        JavaTeacher  jt = new JavaTeacher();
        //子类继承了父类的方法sleep
        jt.setName("张三");
        jt.setAge(30);
        jt.sleep();
        jt.teachJava();
 
        PHPTeacher pt = new PHPTeacher();
        pt.setName("李四");
        pt.setAge(50);
        pt.sleep();
        pt.teachPHP();
    }
 
}
张三老师在睡觉
张三老师正在教java   年龄:30
李四老师在睡觉
李四老师在教PHP   年龄:50

3.super在构造器中的使用

/**
* 父类
*/
class Teacher{
    String name;
 
    int age;
 
 
    //写了这个带参数的构造方法时候,默认的无参数构造器被替换,如果要用到需要自己写
    public Teacher(String name){
        System.out.println("父类的有参数构造器被调用");
        this.name = name;
    }
 
    //重新手写默认无参数的构造器
    public Teacher(){
        System.out.println("父类的默认构造器被调用");
    }
 
    public void sleep(){
        System.out.println("老师在睡觉");
    }
 
}
/**
*
* 子类的定义
*/
//class 子类 extends 父类{
class JavaTeacher extends Teacher{
 
    public JavaTeacher(){
        //super();
        System.out.println("子类的构造器被调用");
    }
 
    public JavaTeacher(String name){
        //在子类的构造器中隐藏了调用super
        //super();
        //调用父类的一个参数的构造器,super一定是在第一行,不写super(参数)的话调用默认的构造器,这样参数name就不能给属性赋值了
        super(name);
        System.out.println("子类构造器被调用");
 
    }
 
    public void teachJava(){
 
        System.out.println(name+"老师正在教java"+"   年龄:"+age);
    }
 
 
}
 
 
 
class ExtendsDemo4{
    public static void main(String[] args){
        JavaTeacher  jt = new JavaTeacher("张三");
        //jt.teachJava();
        JavaTeacher  jt1 = new JavaTeacher();
 
 
    }
 
}
父类的有参数构造器被调用
子类构造器被调用
父类的默认构造器被调用
子类的构造器被调用

Super的特点:

1.子类实例化的过程中父类的构造器先被调用,然后再调用子类的构造器

2.子类的被调用的时候父类的默认构造器就会被调用(父类如果存在着有参数的构造器,一定要把默认构造器显式的定义出来)。

3.子类在调用父类的有参数的构造器的时候使用super(参数列表…),必须显式使用在第一行,这时父类的默认的构造器就不会再被调用了。

4.子类调用父类的无参构造器使用super可以省略,但是有参数的构造器使用super不能省略并且要放在第一行。

  • super关键字的用法 父类和子类有同名方法和同名属性的情况下

    /**
     * 父类
     */
    class Teacher{
    
        String name = "任亮";
    
        int age;
    
    
    
        public void sleep(){
            System.out.println("老师在睡觉");
        }
    
    }
    /**
     *
     * 子类的定义
     */
    //class 子类 extends 父类{
    class JavaTeacher extends Teacher{
    
        String name = "亮哥";
    
        public void teachJava(){
            //默认情况下单独的使用对象的属性时都隐藏了this.
            System.out.println(this.name+"老师正在教java"+"   年龄:"+age);
            System.out.println(super.name+"老师正在教java"+"   年龄:"+age);
            //调用子类的sleep方法
            this.sleep();
            //调用父类的sleep方法
            super.sleep();
        }
    
        public void sleep(){
            System.out.println("Java老师在睡觉");
    
        }
    }
    
    
    
    class ExtendsDemo5{
        public static void main(String[] args){
            JavaTeacher  jt = new JavaTeacher();
            jt.teachJava();
    
    
        }
    
    }
    亮哥老师正在教java   年龄:0
    任亮老师正在教java   年龄:0
    Java老师在睡觉
    老师在睡觉
    

    4.方法的重写override

  • 子类重写父类的方法

/**
* 父类
*/
class Teacher{
 
    String name;
 
    int age;
 
 
 
    public void sleep(){
        System.out.println("老师在睡觉");
    }
 
}
/**
*
* 子类的定义
*/
//class 子类 extends 父类{
class JavaTeacher extends Teacher{
 
    String name;
 
    public void teachJava(){
        //默认情况下单独的使用对象的属性时都隐藏了this.
        System.out.println(this.name+"老师正在教java"+"   年龄:"+age);
 
    }
 
    //父类和子类有相同的方法(方法名、参数个数和类型、返回值,方法体可以不同),
    //子类会覆盖父类的方法,所以外部创建子类调用该方法的时候一定是调用的子类的方法。override
    public void sleep(){
        //在子类中使用super可以调用父类的sleep方法
        //super.sleep();
        System.out.println("Java老师在睡觉");
 
    }
 
}
 
 
 
class Override{
    public static void main(String[] args){
        JavaTeacher  jt = new JavaTeacher();
        jt.sleep();
 
 
    }
 
}

重写和重载的区别

  • 重载overload:在一个类中有两个或者两个以上同名的方法,但是参数不同(两个方法的参数的个数不同或者参数的类型不同),跟返回值无关
  • 重写override:在有继承关系的两个类中父类和子类有相同的方法,子类可以对父类的方法进行重写,使用重写之后子类的方法会覆盖父类的方法

4.final关键字

  • 可以作用在属性,方法,和类上。

final修饰方法

  • final修饰的方法不能被重写,但是子类可以调用父类的final修饰的方法。
class User{
    int username;
 
    int password;
 
    //没有加final的话,如果有个子类重写了这个登录方法的话就能随便登录了。使用final让这个方法不能被重写
    public final void login(int username, int password){
        if(username == 123 && password == 123){
            System.out.println("登录成功");
        }
    }
}
 
class Emp extends User{
 
    int empNo;
 
    /*
    FinalDemo.java:17: 错误: Emp中的login(int,int)无法覆盖User中的login(int,int)
        public void login(int username, int password){
                    ^
     被覆盖的方法为final
    1 个错误
    */
    public void login(int username, int password){
        System.out.println("登录成功");
    }
 
}
 
class FinalDemo{
    public static void main(String[] args){
        Emp e = new Emp();
        e.login(1,1);
    }
 
}
 

final修饰属性

  • final修饰属性时候属性的值不能被改变,通常用在常量上。
  • 常量的定义要全大写,多个单词用下划线分割如CIRCLR_AREA
  • 常量的定义 public static final float PI = 3.14f;
class User{
    int username;
 
    int password;
    //final修饰在属性上,属性的值不能再改变 常量的规范写法
    public static final float PI = 3.14f;
}
 
 
 
class FinalDemo1{
    public static void main(String[] args){
        User.PI = 6.28f;
    }
 
}
 

final修饰类

  • final修饰的类不能被继承
final class User{
    int username;
 
    int password;
 
 
}
 
//final修饰的User不能被继承
class Emp extends User{
 
    int empNo;
 
 
 
}
 
class FinalDemo2{
    public static void main(String[] args){
        Emp e = new Emp();
 
    }
 
}

5.抽象类

  • 抽象通俗说法就是看不懂

  • 为什么我们要使用抽象?答:当多个具体的实体类存在着共同的行为,但是有不同的表现,我们在父类继承过程中父类的方法具体实现不同确定,但是能确定的是他们都有这种行为。

  • 使用abstract关键字

  • 抽象类中也可以有非抽象方法,这时非抽象方法可以有主体。

  • 抽象方法不能有主体 也就是{}

  • 抽象类是不可以实例化的

  • 如果一个子类继承了抽象类就必须实现抽象类中的所有抽象方法

  • 抽象类也可以继承抽象类,这样就不需要实现父抽象类的方法了。

  • 抽象类的抽象方法不能和private,final, static共存。为什么?首先private修饰的属性和方法只能在本类中使用,而抽象类的抽象方法就是为了继承,所以在本类中使用没有意义。final修饰之后不能被重写了,显然无意义。static成为静态方法,类方法之后由于他没有方法体所以也没有意义。抽象类中没有类方法。

  • 一般抽象类中,共有的方法可以不设置为抽象,比如Person的sleep方法,是个人都可以睡觉,但是不同的继承类表现各不相同的要设置为抽象方法。

//抽象类
abstract class Person{
    String name;
 
    int age;
 
    //抽象类的抽象方法不能有方法的主体。
    public abstract void smoking();
 
}
 
//抽象类可以被继承
abstract class Student extends Person{
 
    //抽象类中的非抽象方法可以有方法体
    public void study(){
        System.out.println(name+"正在学习");
    }
 
    public void smoking(){
        System.out.println(name+"正在抽中华");
    }
 
}
 
class Worker extends Person{
 
 
    public void work(){
        System.out.println(name+"正在工作");
 
    }
 
    //如果一个子类继承了抽象类就必须实现抽象类中的抽象方法
    public void smoking(){
        System.out.println(name+"正在抽大前门");
    }
 
}
 
//父类Student中没有抽象方法所以不需要实现,这里study是重写。
class JavaStudent extends Student
{
    public void study(){
        System.out.println(this.name+"正在学习java");
    }
}
 
class AbstractDemo{
    public static void main(String[] args){
        //抽象类是不可以实例化的
        //Person p = new Person();
 
        Worker w = new Worker();
        w.name = "张三";
        w.smoking();
 
 
        JavaStudent s = new JavaStudent();
        s.name = "李四";
        s.study();
 
    }
 
}
张三正在抽大前门
李四正在学习java

14.接口

1.接口和实现类

  • 什么是接口:当一个抽象类,如果抽象类中的所有方法都是抽象的,那么我们就可以把它定义为一个接口,**接口是对行为的抽象。类是对属性和行为的抽象

语法

interface 接口名{

抽象方法定的定义;

….

}

  • 接口的特征

1.接口中的方法的定义不需要Abstract来修饰,默认就是抽象的

2.接口中没有属性

3.接口中的方法不能和private,static和final共存

4.在接口中可以定义属性,可以通过接口的实现类的实例来访问,还可以通过接口名来访问(推荐大家 使用 接口名.属性名),接口中的属性不能修改,我们接口中的属性默认都是final static 的,通常在接口中来定义 属性把它作为常量,常量的名字规范是单词大写,而且多个单词之间用下划线来分隔。比如: FILE_PATH

5.接口可以继承接口(单继承)

  • 接口的实现

class 类名 implements 接口名,接口名,…{
//实现每一个接口中的方法
实现接口的类必须要实现接口中的所有的方法
}

//接口不可以被直接实例化
interface TeachService{
    //在接口中每一个方法的定义都是抽象的,可以省略Abstract
    public void teachLession();
 
    public void smoking();
 
    //接口中定义的属性默认是public static final的常量
    float PI = 3.14f;//相当于public static final float PI = 3.14;
}
 
/**
*
 class 类名    implements 接口名,接口名,…{
        //实现每一个接口中的方法
        实现接口的类必须要实现接口中的所有的方法
    }
*/
//接口的实现类JavaTeacher
class JavaTeacher implements TeachService{
 
    public void teachLession(){
        System.out.println("教java课");
    }
    public void smoking(){
        System.out.println("在抽万宝路");
    }
 
}
 
 
 
 
class InterDemo1{
    public static void main(String[] args){
        JavaTeacher jt = new JavaTeacher();
        jt.teachLession();
        jt.smoking();
        System.out.println(jt.PI);
    }   
 
}
 
教java课
在抽万宝路
3.14

2.接口的继承

  • 接口可以多继承,多级继承,和多实现
  • 接口的实现类要实现与这个类相关的所有接口的方法。
interface A{
 
    public void method();
}
 
interface B{
    public void method1();
}
//接口可以继承
interface C extends A{
    public void method2();
}
 
/**
* jdk1.7中接口可以多继承和多级继承和多实现
   jdk1.6以下接口是不能多继承
*/
interface D extends C,B{
 
}
 
//CImpl实现了D接口和B接口,D又继承了C和B接口。C又继承了A接口,所以CImpl必须实现所有方法。
class CImpl implements D,B{
    public void method(){
 
    }
 
    public void method2(){
 
    }   
 
    public void method1(){
 
    }
}
 
class InterDemo3{
    public static void main(String[] args){
 
    }
}

3.接口的应用

  • 使用接口计算圆和正方形的面积和周长
/**
* 范例:使用接口方式计算圆和正方形的面积和周长。
*/
 
interface CalInter{
    /**
     * 定义计算面积的方法
     */
    public double getArea(double r);
    /**
     * 定义计算周长的方法
     */
    public double getLength(double r);
 
    /**
     * 定义圆周率的常量
     */
    public final static double PI = 3.14;
}
 
class Circle implements CalInter{
    /**
     定义圆的属性半径,对属性进行封装
     */
    private double r;
    /**
     * 定义圆有参数的构造器
     */
    public Circle(double r){
        this.r = r;
    }
    /**
     * 提供一个对外访问r的方法
     */
    public double getR(){
        return r;
    }
 
    public double getArea(double r){
        return r*r*CalInter.PI;
    }
 
    public double getLength(double r){
        return 2*r*CalInter.PI;
    }
 
}
 
/**
* 定义正方形的类
*/
class Rect implements CalInter{
    //对边长属性进行封装
    private double r;
 
    public Rect(double r){
        this.r = r;
    }
 
    public double getR(){
        return r;
    }
 
    public double getArea(double r){
        return r*r;
    }
 
    public double getLength(double r){
        return 4*r;
    }
 
}
 
class InterDemo4{
    public static void main(String[] args){
        Circle c = new Circle(10);
        //获得半径
        double r = c.getR();
        //计算面积
        double area = c.getArea(r);
        //计算周长
        double length = c.getLength(r);
        System.out.println("半径是"+r+"的圆的面积是"+area);
        System.out.println("半径是"+r+"的圆的周长是"+length);
 
        System.out.println("------------------------");
 
        Rect rect = new Rect(8);
        //获得边长
        double blength = rect.getR();
        //计算面积
        double barea = rect.getArea(blength);
        //计算周长
        double totalLength = rect.getLength(blength);
        System.out.println("边长是"+blength+"的正方形的面积是"+barea);
        System.out.println("边长是"+blength+"的正方形的周长是"+totalLength);
    }
}

4.接口的好处

  • 接口打破了类的单继承的局限性。一个类可以实现多个接口。

  • 使用接口的好处:

    1. 接口定义的是一种标准,可以使我们的代码分层开发,分模块开发。

    2. 降低代码的耦合度,提高代码的可扩展性和可维护性

    3. 接口改进了单继承的局限。

interface A{
    public void method();
}
 
 
interface B{
    public void method();
}
 
/**
*接口是可以多继承的,因为都没有方法体
*/
//class C extends A,B会报错
interface C extends A,B{
 
 
}
 
class D implements C{
    public void method(){
 
    }
 
 
}
 
 
class InterDemo6{
    public static void main(String[] args){
 
    }
}

5.接口和抽象类的区别

接口和抽象类的区别:

1.接口的所有方法都是抽象的,抽象类里面的方法可以是抽象的也可以是不抽象的。

2.接口和抽象类都不能实例化,接口需要类来实现后实例化实现类,抽象类需要类来继承然后实例化子类。

3.抽象类只能单继承,接口可以多继承接口(jdk1.7),接口还可以多实现。

4.接口中的属性是public static final类型的,抽象类中的属性跟普通类中的属性没有区别。

5.接口中的方法默认就是抽象的不需要加absract,抽象类中的抽象方法需要加abstract关键字。

15.多态

  • 多态就是行为具有表现多种功能的能力。
  • 同一方法作用于不同对象会有不同的解释,得到不同的执行结果。

1.继承多态

我们通常使用抽象类来抽象要使用多态的方法

继承多态的特点:

1.必须要有继承关系,在抽象类中可以定义多态的抽象方法,通过子类来继承这个抽象类然后复写抽象类中的抽象方法以达到多态的效果。

2.多态子类的实例可以赋给父类的引用

//继承的多态要用到抽象类的抽象方法
abstract class Teacher{
 
    String name;
 
    int age;
    /**
     *继承多态的抽象方法teachLession()讲课
     */
    public abstract void teachLession();
 
    public void sleep(){
        System.out.println(name +"正在睡觉");
    }
 
}
 
class JavaTeacher extends Teacher{
    /**
     * 实现了讲课的第一种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲多态");
    }
 
}
 
class PHPTeacher extends Teacher{
    /**
     * 实现了讲课的第二种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲PHP");
    }
 
 
}
 
class Leader{
 
    /**
     * 考察java老师讲课情况
     */
    public void checkJavaTeacher(JavaTeacher jt){
        System.out.println("开始考察");
        //开始讲课
        jt.teachLession();
        System.out.println("考察结束");
    }
    /**
     * 考察PHP老师讲课情况
     */
    public void checkPHPTeacher(PHPTeacher pt){
        System.out.println("开始考察");
        //开始讲课
        pt.teachLession();
        System.out.println("考察结束");
    }
 
 
}
 
 
class InterDemo8{
    public static void main(String[] args){
        JavaTeacher jt = new JavaTeacher();
        jt.name = "张三";
 
 
 
        PHPTeacher pt = new PHPTeacher();
        pt.name = "李四";
 
 
        //创建领导对象
        Leader leader = new Leader();
        //领导考察java老师张三
        leader.checkJavaTeacher(jt);
        leader.checkPHPTeacher(pt);
 
    }
}
  • 上面的代码领导对不同的老师有不同的方法来调用进行考察,那么如何运用多态来简化呢。见2

2.子类的实例赋给父类的引用

  • 可以通过instanceof关键字来预知当前父类变量引用的子类的类型。
abstract class Teacher{
 
    String name;
 
    int age;
    /**
     *继承多态的抽象方法
     */
    public abstract void teachLession();
 
    public void sleep(){
        System.out.println(name +"正在睡觉");
    }
 
}
 
class JavaTeacher extends Teacher{
    /**
     * 实现了讲课的第一种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲多态");
    }
 
    //JavaTeacher类多加的方法
    public void smoking(){
        System.out.println(name+"正在抽烟");
    }
}
 
class PHPTeacher extends Teacher{
    /**
     * 实现了讲课的第二种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲PHP");
    }
 
 
}
 
class Leader{
 
    String teacher = " ";
    /**
     * 考察老师讲课情况
     */
    public void checkTeacher(Teacher t){
 
        if(t instanceof JavaTeacher){
            teacher = "java老师";
        }else if(t instanceof PHPTeacher){
            teacher = "php老师";
        }
        System.out.println("开始考察"+teacher);
        //开始讲课
        t.teachLession();
        System.out.println("考察结束");
    }
 
 
}
 
 
class InterDemo8{
    public static void main(String[] args){
        //子类的实例赋给父类的引用jt
        Teacher jt = new JavaTeacher();
        jt.name = "张三";
 
        Teacher pt = new PHPTeacher();
        pt.name = "李四";
 
 
        //创建领导对象
        Leader leader = new Leader();
        //领导考察Java和PHP两个老师
        leader.checkTeacher(jt);
        leader.checkTeacher(pt);
        /*出错,子类中的特有方法不能通过父类来调用*/
        //jt.smoking();
 
    }
}
开始考察java老师
张三正在讲多态
考察结束
开始考察php老师
李四正在讲PHP
考察结束

3.接口多态

  • 就是把抽象类写成接口就行了
//接口中没有name,因为要是设置了就是public static final类型了。
interface TeacherInter{
 
    public void teachLession();
 
    public void sleep();
 
}
 
class JavaTeacher implements TeacherInter{
 
    String name;
 
    int age;
 
    //构造的时候对属性进行赋值
    public JavaTeacher(String name){
        this.name = name;
    }
 
    /**
     * 实现了讲课的第一种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲多态");
    }
 
    public void sleep(){
 
        System.out.println(name+"在睡觉");
    }
 
    public void smoking(){
        System.out.println(name+"正在抽烟");
    }
 
}
 
class PHPTeacher implements TeacherInter{
    String name;
 
    int age;
 
    public PHPTeacher(String name){
        this.name = name;
    }
 
    /**
     * 实现了讲课的第二种形态
     */
    public void teachLession(){
        System.out.println(name + "正在讲PHP");
    }
 
    public void sleep(){
        System.out.println(name+"睡绳子");
    }
 
}
 
class Leader{
 
    public void checkTeacher(TeacherInter t){
        String teacher = "";
        //instanceof可以判断我们多态的引用变量到底是什么类型
        if(t instanceof JavaTeacher){
            teacher = "java老师";
        }else if(t instanceof PHPTeacher){
            teacher = "PHP老师";
        }
        System.out.println("开始考察"+teacher);
        //老师开始讲课
        t.teachLession();
        System.out.println("考察结束");
 
    }
 
 
}
 
 
class InterDemo10{
    public static void main(String[] args){
        //子类的实例用父类的类型来接收
        TeacherInter jt = new JavaTeacher("张三");
 
        TeacherInter pt = new PHPTeacher("李四");
 
        //子类中的特有方法不能通过父类来调用
        //jt.smoking();错误
 
        Leader leader = new Leader();
        leader.checkTeacher(pt);
        leader.checkTeacher(jt);
 
    }
}
开始考察PHP老师
李四正在讲PHP
考察结束
开始考察java老师
张三正在讲多态
考察结束

16.模板模式

  • 模板模式就是使用的多态
  • 模板就是一个模子
  • 模板模式有两个角色 抽象模板和具体模板

第一种角色:抽象模板

定义三种方法:第一个抽象方法需要子类去实现,子类必须实现

第二个扩展方法,是一个空方法,子类可以去复写也可以不复写

第三个私有的最终方法,不可复写的

第四个模板方法,这个方法中定义了每一个方法的执行顺序和逻辑

第二种角色:具体模板 要继承抽象模板

需要实现抽象模板中的抽象方法,可以扩展抽象模板中的扩展方法。

模板的例子

/**
* 抽象模板
*/
abstract class AbstractTemplate{
 
    /**
     * 必须要实现的一个业务方法
     */
     public abstract void abstractMethod();
 
     /**
      * 定义一个可以去复写也可以不复写的方法
      */
     public void hookMethod(){
 
     }
 
     /**
      * 必须不能被重写的方法
      */
      public final void concrateMethod(){
 
      }
 
      /**
       * 模板方法,供其他类来使用
       */
      public void templateMethod(){
          hookMethod();
          abstractMethod();
          concrateMethod();
      }
}
 
 
/**
* 具体的模板 要继承抽象模板
*/
class ContrateTemplate extends AbstractTemplate{
 
     /**
      *实现具体的业务逻辑
      */
     public void abstractMethod(){
         System.out.println("具体逻辑");
     }
 
    //可以实现也可以不实现
     public void hookMethod(){
        System.out.println("方法重写");    
    }
 
}
 

模板的应用

  • 计算一个方法的运行时间可以用模板模式
/**
* 抽象模板
*/
abstract class CalTimeAbsTemplate{
 
    /**
     * 必须要实现的一个业务方法
     */
     public abstract void doJob();
 
     /**
      * 定义一个可以重写也可以不重写的方法,这里做一个提示
      */
     public void hookMethod(){
             System.out.println("程序开始运行");
     }
 
     /**
      * 必须不能被重写的方法
      */
      public final long concrateMethod(){
          long currentTime = System.currentTimeMillis();
          return currentTime;
      }
 
      /**
       * 模板方法,供其他类来使用
       */
      public long templateMethod(){
        //提示程序开始运行
          hookMethod();
          //获得doJob执行前的时间
          long startTime = concrateMethod();
          doJob();
          //获得doJob执行后的时间
          long endTime = concrateMethod();
          return endTime - startTime;
 
      }
}
 
 
/**
* 具体的模板
*/
class ContrateTemplate extends CalTimeAbsTemplate{
 
     /**
      *实现具体的业务逻辑
      */
     public void doJob(){
         for(int i = 0; i < 10; i++){
             System.out.println("程序正在执行"+i+"%");
         }
     }
}
 
class TemplateDemo1{
    public static void main(String [] args){
        //创建具体模板对象
        ContrateTemplate ct = new ContrateTemplate();
        //获得具体模板中的doJob的运行时间
        long doTime = ct.templateMethod();
        System.out.println("程序的运行时间是:"+doTime+"毫秒");
    }
 
}
程序正在执行0%
程序正在执行1%
程序正在执行2%
程序正在执行3%
程序正在执行4%
程序正在执行5%
程序正在执行6%
程序正在执行7%
程序正在执行8%
程序正在执行9%
程序的运行时间是:2毫秒
  • 抽象模板有四个方法,一个是必须重写的抽象方法一般用于实现一个具体业务,一个是可重写可不重写的方法hookMethod,一个是不能被重写的方法,一个是模板方法一般用于对具体业务进行操作,就是把前面三个方法排一下就行了。

17.API的使用

  • Api是我们写代码的帮助文档。后续要接触很多第三方的技术,他们都会给我们提供api,我们就必须会读懂。Jdk也给我们提供了帮助文档。

    通过索引的方式来根据类名来查找类的api信息

打开API在索引中搜索Math或者math你会找到java.lang包里面的Math类,这里面字段摘要就是属性,方法摘要就是方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBQ6CI6K-1596104376204)(https://ae01.alicdn.com/kf/Hdbf3fbc7b7ff4af599570a236a168ce5V.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJJblAIC-1596104376207)(https://ae01.alicdn.com/kf/Hf614f688a07b4ebdbeec14181a3013f2K.jpg)]

  • API中的方法一般都是static修饰的类方法,调用时可以直接类名调用

对Math类API提供的常用属性和方法有:

自然对数:Math.E

圆周率:Math.PI

绝对值:Math.abs(-1.5)

把小数去掉整数加1 Math.ceil(12.1)

把小数位舍弃 Math.floor(12.9)

比较两个数的大小 Math.max(12.9, 27)

比较两个数的大小 Math.min(12.9, 27)

计算某个数的n次幂 Math.pow(3, 3)

四舍五入 Math.round(4.5)

开平方 Math.sqrt(16));

使用Math类

//java.lang.*下面的所有类都不需要引入,都是自动引入的。System类也在lang包里面
class MathDemo{
 
    public static void main(String[] args){
        double pi = Math.PI;
        System.out.println(pi);
        System.out.println(Math.E);
 
        float absValue = Math.abs(-1.4f);
        System.out.println("绝对值是:"+absValue);
 
        //把小数位去掉后的整数加一
        double ceilVal = Math.ceil(1.8);
        System.out.println("ceil值:"+ceilVal);
        //小数位舍弃
        double floorVal = Math.floor(1.9);
        System.out.println("floor值:"+floorVal);
 
        int maxVal = Math.max(12,12);
        //比较大小值
        System.out.println("最大值是:"+maxVal);
 
        int minVal = Math.min(12,34);
        //比较大小值
        System.out.println("最大值是:"+maxVal);
 
        double powVal = Math.pow(3,3);
        System.out.println("n次幂:"+powVal);
 
        //四舍五入
        int roundVal = Math.round(12.6f);
        System.out.println("四舍五入的结果:"+roundVal);
 
        double sqrt = Math.sqrt(9);
        System.out.println("开平方:"+sqrt);
    }
}
 
3.141592653589793
2.718281828459045
绝对值是:1.4
ceil值:2.0
floor值:1.0
最大值是:12
最大值是:12
n次幂:27.0
四舍五入的结果:13
开平方:3.0

18.包的使用

包:就是文件夹

作用:对类做分类管理,可以区分同名不同包的类。

语法: package 包名(不同级别用“.”来分隔);,如 package com.rl;, 位置放置文件的有效代码第一行(注释不算)

如何编译:

Javac –d . 源文件

包的简单使用

不同包下的类如何访问

//ArrayUtils类在com.tx.array包中 .代表多级目录 不同级别用“.”来分隔
package com.tx.array;
//class 是public的才可以被外部的包的类引用
public class ArrayUtils{
 
 
    public static void printArr(int [] arr){
        for(int i = 0; i < arr.length; i++)
        System.out.println(arr[i]+"\t");   
    }
 
}
//ArrayDemo在cn.tx.array包中
//给本类打包
package cn.tx.array;
//引入外部包的类
import com.tx.array.ArrayUtils;
class ArrayDemo{
 
    public static void main(String[] args){
        int[] arr = {4,5,6,7,8,9};
        //类前面带着包名来引入类
        //com.tx.array.ArrayUtils.printArr(arr);
        //使用import之后里面的类方法直接类名调用。
        ArrayUtils.printArr(arr);
    }
}
  • 使用javac -d . ArraysUtils.java和javac -d . ArraysDemo.java 来编译在当前目录生成包,包里面是字节码文件,注意前后顺序,import的包要先编译
  • 要是有多个包必须在最外层运行不然找不到import的包,运行时要加上包名,找到对应的字节码文件才行 java cn.tx.array.ArrayDemo

同一包下的类如何访问

  • 在同一包下的两个类可以直接访问,不需要用import,不同的包才需要导包。

19.Scanner扫描器类

  • 查API 索引 搜索scanner
import java.util.Scanner;
class ScannerDemo{
 
 
    public static void main(String[] args){
        //创建扫描器的对象
        Scanner sc = new Scanner(System.in);
        //获得从控制台输入的一个整数
        /*
        int val = sc.nextInt();
        System.out.println("输出数字:"+val);
 
        float val1 = sc.nextFloat();
        System.out.println("输出数字:"+val1);
 
        System.out.println("val+val1="+(val+val1));
        */
        //获得控制台上输入的字符串 这里要注释掉前面的才能读取字符串因为不注释前面的会有一个换行符号
        String line = sc.nextLine();
        System.out.println("输入的结果是:"+line);
    }
}

20.内部类

  • 类内部的类就是内部类。

  • 位置:把一个类定义到另一个类中,那么内部的类就是内部类。

    注意:内部类不能直接创建

    创建内部类的语法:

    外部类.内部类 变量名 = new 外部类对象.new内部类对象

    内部类的外部类的方法如果想要访问内部类的方法,必须创建内部类的对象,根据内部类的对象来访问

1.内部类的使用

class Outter{
 
    private static int num = 1;
 
    //定义内部类
    class Inner{
        //定义属性
        int num1;
        //定义内部类的方法
        public void show(){
            System.out.println("通过内部类的方法show打印num1:"+num1);
            System.out.println("通过内部类的方法show打印外部类属性num:"+num);
        }
 
    }
 
    public void method(){
        System.out.println("method方法的调用");
        //在内部类所属的外部类中可以创建内部类的对象
        Inner inner = new Inner();
        inner.show();
 
    }
 
}
 
class OutterDemo{
    public static void main(String[] args){
        //内部类不可以直接创建对象
        //Inner inner = new Inner();
        //外部类.内部类 变量名 = new 外部类对象.new内部类对象
        Outter.Inner inner = new Outter().new Inner();
        //访问内部类的属性
        System.out.println(inner.num1);
        //调用内部类的方法show
        inner.show();
        System.out.println("-------------------------");
        Outter outter = new Outter();
        outter.method();
 
    }
 
}
 
0
通过内部类的方法show打印num1:0
通过内部类的方法show打印外部类属性num:1
-------------------------
method方法的调用
通过内部类的方法show打印num1:0
通过内部类的方法show打印外部类属性num:1
 
  • 内部类编译之后会生成一个Outter$Inner.class的字节码文件

2.私有内部类

//外部的类的权限修饰只能是public或者默认
class Outter{
 
    private static int num = 1;
 
    //内部类可以是private,定义了一个私有内部类,只能在Outter中被创建,其他的类不能创建
    private class Inner{
 
        int num1 = 8;
 
        public void show(){
            System.out.println(num1);
        }
 
    }
 
    public void method(){
        //创建私有内部类的对象
        Inner inner = new Inner();
        inner.show();
 
    }
 
 
 
}
 
class OutterDemo1{
    public static void main(String[] args){
        //创建内部类的对象
        //出现错误,因为Inner是私有的,所有Inner只能在Outter内部被创建
        //Outter.Inner inner = new Outter().new Inner();
        Outter out = new Outter();
        out.method();
    }
 
}

3.静态内部类

class Outter{
 
    private static int num = 1;
 
    //内部类可以是static,定义了一个静态内部类,静态内部类只能访问它的外部类的静态属性。
    static class Inner{
        int num1 = 2;
 
        public void show(){
            System.out.println("show方法被调用"+num);
        }
 
 
        public static void show1(){
            System.out.println("show1静态方法被调用"+num);
        }
    }
 
    public void method(){
        Inner inner = new Inner();
        inner.show();
        inner.show1();
 
    }
 
 
}
 
class OutterDemo2{
    public static void main(String[] args){
        //内部类的对象创建,外部类名.内部类名 变量名 = new 外部类名.内部类对象
        Outter.Inner inner = new Outter.Inner();
        inner.show();
        inner.show1();
        //静态内部类的静态方法的调用,外部类名.内部类名.静态方法名();
        Outter.Inner.show1();
        System.out.println("-------------------------");
        Outter out = new Outter();
        out.method();
    }
 
}
show方法被调用1
show1静态方法被调用1
show1静态方法被调用1
-------------------------
show方法被调用1
show1静态方法被调用1
  • 静态内部类只能访问它的外部类的静态属性。

4.局部内部类

  • 定义在方法中的类
 
 
class Outter{
 
    private static int num = 4;
 
    public void method(){
        //在方法中定义的类叫做局部内部类 局部内部类
        class Inner{
            int num1 = 5;
            public void show(){
                System.out.println(num1);
            }
        }
        //创建局部内部类的对象
        Inner inner = new Inner();
        //调用局部内部类的方法
        inner.show();
 
 
    }
 
 
}
 
class OutterDemo4{
    public static void main(String[] args){
        Outter outter = new Outter();
        outter.method();
 
    }
 
}

5.匿名内部类

  • 一个类没有名字,并且定义在一个类中

    什么是匿名类:没有名字的类,这种类需要在接口上实现。

    匿名类和匿名内部类都需要接口或者抽象类的支持。

    创建一个匿名的类的对象这个类的对象实现OuterInter的接口,在大括号中实现接口中的方法,方法调用完毕后就会被垃圾回收

interface MyInter{
 
    public void method();
 
}
 
class MyClass implements MyInter{
 
    public void method(){
        System.out.println("我是实现了method方法");
    }
}
 
class OutterDemo3{
    public static void main(String[] args){
        //接口的实现类MyClass掉一共method方法
        new MyClass().method();
 
        //创建匿名对象调用method,这个对象很快就会被GC回收
        //接口是不能实例化,在匿名类创建的时候我们可以借助接口
        //创建了一个没有名字但是实现了MyInter接口的类的对象, 用完就会被回收掉,所以马上就要调用方法才有意义。
        //创建实现了MyInter接口的匿名内部类对象
        new MyInter(){
            int i = 1;
            //对接口的抽象方法实现
            public void method(){
                System.out.println("匿名类的对象的方法被调用"+i);
            }
        }.method();
 
        System.out.println("----------------------------");
        //创建匿名类的对象赋值被接口引用变量,这样这个匿名内部类在new之后不会立即被GC回收
        MyInter mi = new MyInter(){
            int i = 3;
            public void method(){
                System.out.println("匿名类1的对象的方法被调用"+i);
            }
        };
        mi.method();
 
    }
 
}
 
我是实现了method方法
匿名类的对象的方法被调用1
----------------------------
匿名类1的对象的方法被调用3

21.访问权限修饰符

一共有四种 private 默认 protected public

权限修饰符可以作用在哪些概念上

  • 下表说明权限修饰符能否修饰

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eVKUR6gE-1596104376213)(https://ae01.alicdn.com/kf/H2b25cee51c694ec7bc39ffe66048611a3.jpg)]

  • 在类上如果同public来修饰,那么这个类可以被任何类所访问。类如果是默认修饰在本类中可以访问,在同一个包下的类可以访问。其余都不可以访问

四种访问权限修饰符的作用范围

  • 我们通常情况:类我们一般设置成public的,属性一般都是private, 方法一般都是public的,也有少数使用(protect,private)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jICYsanH-1596104376215)(https://ae01.alicdn.com/kf/H54f2d17a04c940f5a05c4b466dafe51dM.jpg)]

public class Perm{
    //private
    private void privateMethod(){
        System.out.println("private修饰的方法可以被调用");
    }
    //默认
    void defaultMethod(){
        System.out.println("默认修饰的方法可以被调用");
    }
    //protected
    protected void protectedMethod(){
        System.out.println("protected修饰的方法可以被调用");
    }
    //public
    public void publicMethod(){
        System.out.println("public修饰的方法可以被调用");
    }
 
    //在同一个类中都可以访问
    public static void test(){
        Perm p = new Perm();
        p.privateMethod();
        p.defaultMethod();
        p.protectedMethod();
        p.publicMethod();
    }
 
}
 
class Test{
    public static void main(String[] args){
        Perm.test();
        System.out.println("--------------");
        Perm p = new Perm();
        //同包不同类不能访问private修饰的方法
        //p.privateMethod();
        p.defaultMethod();
        p.protectedMethod();
        p.publicMethod();
    }
}
private修饰的方法可以被调用
默认修饰的方法可以被调用
protected修饰的方法可以被调用
public修饰的方法可以被调用
--------------
默认修饰的方法可以被调用
protected修饰的方法可以被调用
public修饰的方法可以被调用
 
  • 不同包下

    package ct.ui;
    import com.tx.Perm2;
    public class TestPerm3 extends Perm2{
    
        public static void main(String[] args){
            TestPerm3 p = new TestPerm3();
            //p.privateMethod();
            //p.defaultMethod();
            p.protectedMethod();
            p.publicMethod();
    
        }
    
    }
    
    package com.tx;
    public class Perm2 {
    
        private void privateMethod(){
            System.out.println("private修饰的方法可以被调用");
        }
    
        void defaultMethod(){
            System.out.println("默认修饰的方法可以被调用");
        }
    
        protected void protectedMethod(){
            System.out.println("protected修饰的方法可以被调用");
        }
    
        public  void publicMethod(){
            System.out.println("public修饰的方法可以被调用");
        }
    
    }
    
    输出
    protected修饰的方法可以被调用
    public修饰的方法可以被调用
    

22.Object类

  • 所有类的超类(父类和父类以上的类),在java lang包里面
  • Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符**“@”此对象哈希码的无符号十六进制**表示组成。换句话说,该方法返回一个字符串,它的值等于:
  • hashCode()返回该对象的哈希码值。
  • equals()指示其他某个对象是否与此对象“相等”。返回是一个boolean类型的值

1.toString方法 hashCode方法 equals方法

/**
* java.lang.Object类是默认被每一个类继承的
*/
//这里影藏了extends Object
class Student{
 
    private String name;
 
    private int age;
 
    private int gender;
 
    public Student(){
        //这里不写的话就默认调用super();
        super();
    }
 
    public void setName(String name){
        this.name = name;
    }
 
    public String getName(){
        return name;
    }
 
    public void setAge(int age){
        this.age = age;
    }
 
    public int getage(){
        return age;
    }
 
    public void setGender(int gender){
        this.gender = gender;
    }
 
    public int getGender(){
        return gender;
    }
 
    /**
    *一般要重写toString方法
    */
    /*
    public String toString(){
        return this.name + " " + this.age + " "+ this.gender;
    }*/
 
}
 
class ObjectDemo{
 
    public static void main(String[] args){
        Student s = new Student();
        Student s1 = new Student();
        //使用学生的对象来调用从父类继承下来的toString方法,
        //默认情况下我们认为Object的toString打印的就是对象的地址。
        String str = s.toString();
        System.out.println(str);
        //直接输出对象默认调用了对象的toString方法
        System.out.println(s);
        //获得该对象的哈希码
        int hashCode = s.hashCode();
        int hashCode1 = s1.hashCode();
        System.out.println("学生对象的哈希值:"+hashCode);
        System.out.println("学生1对象的哈希值:"+hashCode1);
           //返回true;
        System.out.println(s.equals(s1));
    }
}
Student@97d01f 如果没有重写默认使用Object的toString方法
Student@97d01f
学生对象的哈希值:9949215 它的十六进制就是97d01f
学生1对象的哈希值:14721926

2.重写equals方法和hashCode方法

  • equals(Object obj)指示其他某个对象是否与此对象“相等”。 Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y**,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)**。 见上例子
  • 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
/**
* java.lang.Object类是默认被每一个类继承的
*/
class Student{
 
    private String name;
 
    private int age;
 
    private int gender;
 
    public Student(){
        super();
    }
 
    public void setName(String name){
        this.name = name;
    }
 
    public String getName(){
        return name;
    }
 
    public void setAge(int age){
        this.age = age;
    }
 
    public int getAge(){
        return age;
    }
 
    public void setGender(int gender){
        this.gender = gender;
    }
 
    public int getGender(){
        return gender;
    }
 
    /**
     * 重写Object的equals方法,判断两个对象的属性是否相等,如果相等就返回true
     */
    public boolean equals(Object obj) {
        boolean result = false;
        //判断Object的实例的类型是否是Student
          if(obj instanceof Student){
            Student student = (Student)obj;
            //在本类中可以访问private的属性 这里name是String类是个对象,可以用Object的equals方法
            if(this.name.equals(student.name)&&(this.age == student.age)&&(this.gender == student.gender)){
                result  = true;
            }
        }
        return result;
  }
  /**
   * 在重写equals方法时最好也重写hashCode
   */
  public int hashCode(){
     return 1;
  }
}
 
class ObjectDemo2{
 
    public static void main(String[] args){
        Student s = new Student();
 
        Student s1 = new Student();
        //直接打印对象时是默认的在调用父类继承下来的toString
        System.out.println(s);
        System.out.println(s1);
 
        //判断两个对象是否相等一般使用equals方法
 
        //boolean isEqual = s.equals(s1);
        //System.out.println(isEqual);
 
        System.out.println("-------------------");
        s.setName("张三");
        s.setAge(10);
        s.setGender(1);
 
        s1.setName("张三");
        s1.setAge(10);
        s1.setGender(1);
        //s1.name = "uuu";
        System.out.println("学生s和学生s1是否相等:"+s.equals(s1));
        //判断对象是否相等不能使用"=="因为"=="是判断的两个对象的地址,两个对象的地址永远不能相等
        System.out.println(s == s1);
        System.out.println(s.hashCode());
        System.out.println(s1.hashCode());
    }
}
Student@1
Student@1
-------------------
学生s和学生s1是否相等:true
false
1
1

3.finalize方法

  • protected修饰的方法,可以作用到不同包的子类

  • finalize方法当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  • 对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。

  • 面试题:说一下final finalize finally的区别? 答:final是一个关键字,他修饰的类不能被继承,它修饰的属性值不能更改,它修饰的方法不能重写。finalize用于垃圾回收,如果对象在堆中没有任何引用时JVM会调用finalize来回收这个对象。finally一般和try catch连用,不管是否发生异常都会被执行。

class Person{
    /**
    *重写Object的finalize方法
    */
    public void finalize(){
        System.out.println("对象将被作为垃圾回收.....");
    }
}
 
class FinalizeDemo{
    public static void main(String[] args){
        Person p = new Person();
        Person p1 = new Person();
        //让对象成为垃圾对象 让对象在堆中没有任何引用
        p = null;
        p1 = null;
        //手动调用GC
        System.gc();
        for(int i=0; i<1000000; ++i){
        //延长程序运行时间,为看到结果
        }
    }
 
}
对象将被作为垃圾回收.....
对象将被作为垃圾回收.....

gender;
}

public int getGender(){
    return gender;
}

/**
*一般要重写toString方法
*/
/*
public String toString(){
    return this.name + " " + this.age + " "+ this.gender;
}*/

}

class ObjectDemo{

public static void main(String[] args){
    Student s = new Student();
    Student s1 = new Student();
    //使用学生的对象来调用从父类继承下来的toString方法,
    //默认情况下我们认为Object的toString打印的就是对象的地址。
    String str = s.toString();
    System.out.println(str);
    //直接输出对象默认调用了对象的toString方法
    System.out.println(s);
    //获得该对象的哈希码
    int hashCode = s.hashCode();
    int hashCode1 = s1.hashCode();
    System.out.println("学生对象的哈希值:"+hashCode);
    System.out.println("学生1对象的哈希值:"+hashCode1);
       //返回true;
    System.out.println(s.equals(s1));
}

}
Student@97d01f 如果没有重写默认使用Object的toString方法
Student@97d01f
学生对象的哈希值:9949215 它的十六进制就是97d01f
学生1对象的哈希值:14721926

 
## 2.重写equals方法和hashCode方法
 
- equals(Object obj)指示其他某个对象是否与此对象“相等”。 Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y**,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)**。 见上例子
- 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
 
```java
/**
* java.lang.Object类是默认被每一个类继承的
*/
class Student{
 
    private String name;
 
    private int age;
 
    private int gender;
 
    public Student(){
        super();
    }
 
    public void setName(String name){
        this.name = name;
    }
 
    public String getName(){
        return name;
    }
 
    public void setAge(int age){
        this.age = age;
    }
 
    public int getAge(){
        return age;
    }
 
    public void setGender(int gender){
        this.gender = gender;
    }
 
    public int getGender(){
        return gender;
    }
 
    /**
     * 重写Object的equals方法,判断两个对象的属性是否相等,如果相等就返回true
     */
    public boolean equals(Object obj) {
        boolean result = false;
        //判断Object的实例的类型是否是Student
          if(obj instanceof Student){
            Student student = (Student)obj;
            //在本类中可以访问private的属性 这里name是String类是个对象,可以用Object的equals方法
            if(this.name.equals(student.name)&&(this.age == student.age)&&(this.gender == student.gender)){
                result  = true;
            }
        }
        return result;
  }
  /**
   * 在重写equals方法时最好也重写hashCode
   */
  public int hashCode(){
     return 1;
  }
}
 
class ObjectDemo2{
 
    public static void main(String[] args){
        Student s = new Student();
 
        Student s1 = new Student();
        //直接打印对象时是默认的在调用父类继承下来的toString
        System.out.println(s);
        System.out.println(s1);
 
        //判断两个对象是否相等一般使用equals方法
 
        //boolean isEqual = s.equals(s1);
        //System.out.println(isEqual);
 
        System.out.println("-------------------");
        s.setName("张三");
        s.setAge(10);
        s.setGender(1);
 
        s1.setName("张三");
        s1.setAge(10);
        s1.setGender(1);
        //s1.name = "uuu";
        System.out.println("学生s和学生s1是否相等:"+s.equals(s1));
        //判断对象是否相等不能使用"=="因为"=="是判断的两个对象的地址,两个对象的地址永远不能相等
        System.out.println(s == s1);
        System.out.println(s.hashCode());
        System.out.println(s1.hashCode());
    }
}
Student@1
Student@1
-------------------
学生s和学生s1是否相等:true
false
1
1

3.finalize方法

  • protected修饰的方法,可以作用到不同包的子类

  • finalize方法当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  • 对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。

  • 面试题:说一下final finalize finally的区别? 答:final是一个关键字,他修饰的类不能被继承,它修饰的属性值不能更改,它修饰的方法不能重写。finalize用于垃圾回收,如果对象在堆中没有任何引用时JVM会调用finalize来回收这个对象。finally一般和try catch连用,不管是否发生异常都会被执行。

class Person{
    /**
    *重写Object的finalize方法
    */
    public void finalize(){
        System.out.println("对象将被作为垃圾回收.....");
    }
}
 
class FinalizeDemo{
    public static void main(String[] args){
        Person p = new Person();
        Person p1 = new Person();
        //让对象成为垃圾对象 让对象在堆中没有任何引用
        p = null;
        p1 = null;
        //手动调用GC
        System.gc();
        for(int i=0; i<1000000; ++i){
        //延长程序运行时间,为看到结果
        }
    }
 
}
对象将被作为垃圾回收.....
对象将被作为垃圾回收.....
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值