自学JAVA知识点总结 - 初级

编程语言6天前更新 AaronHzy
189 0

本文部分内容来自“北京尚学堂,本文不做任何商业用途,禁止转载

第一章 JAVA入门

第二章 数据类型与运算符

第三章 控制语句

第四章 面向对象

第五章 初级提升

测试题


JAVA入门 - 了解JAVA语言

JAVA语言介绍

  • Java由美国SUN公司于1995年开发。
  • Java具有简单性、健壮性、安全性、面向对象、分布式、平台独立与可移植性、多线程、动态性、跨平台等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等,被广泛应用于企业级软件开发、安卓移动开发、大数据云计算等领域,几乎涉及IT所有行业。

JAVA版本介绍

  • JavaSE(Java Platform,Standard Edition):标准版,定位在个人计算机的应用,用于开发和部署桌面、服务器以及嵌入设备和实时环境中的Java应用程序。
  • JavaEE(Java Platform,Enterprise Edition):企业版,定位在企业的应用,开发和部署可移植、健壮、可伸缩且安全的服务器端 Java应用程序。
  • JavaME(Java Platform,Micro Edition):微型版,定位在消费型电子产品的应用,是为机顶盒、移动电话和PDA之类嵌入式消费电子设备提供的Java语言平台。
  • JavaCard(Java Platform,Card):主要是让智慧卡或与智慧卡相近的装置上,以具有安全防护性的方式来执行小型的Java Applet,此技术也被广泛运用在SIM卡、提款卡上。

JAVA运行机制

  • JVM(Java Virtual Machine):JVM是Java语言的虚拟机,用来解释并执行编译好的字节码。
  • JRE(Java Runtime Environment):JRE是Java语言的运行环境,包含JVM、库函数。
  • JDK(Java Development Kit):JDK是Java语言的软件开发工具包,用于开发JAVA程序,其中包含了Java的运行环境和Java工具。
  • JDK包含→ JRE包含→ JVM,如图所示 ↓
  • 自学JAVA知识点总结 - 初级

  • 运行机制:计算机高级语言主要有编译型、解释型这两种类型,而Java是两种类型的结合。
  • 利用文本编辑器编写后缀为.java的Java源程序
  • 再利用编译器(javac)将源程序编译成后缀为.class字节码文件
  • 最后利用JVM(虚拟机/解释器)解释并执行程序
  • 自学JAVA知识点总结 - 初级

  • ava的优势:由于各系统底层运行平台的差别,Java为不同的操作系统提供适配的JVM,编译好的Java程序可以直接在JVM上解释并运行,而不用考虑系统的差别,实现了“一次编译,随处运行”,原理如图所示 ↓
  • 自学JAVA知识点总结 - 初级


JAVA入门 - JAVA开发环境

JAVA开发环境配置

  1. 安装JDK,选择想要的版本下载后安装,下载地址
  2. 了解JDK目录中各文件的作用
  3. 自学JAVA知识点总结 - 初级

  4. 配置Windows系统的环境变量并测试,右键“此电脑”选择“属性” -> 选择“高级系统设置” -> 选择“环境变量”自学JAVA知识点总结 - 初级
  5. 安装编辑器Eclipse或其他IDE,下载地址
  6. Java官方文档,点击打开

数据类型与运算符 - 注释、标识符、关键字

注释

  • 注释的作用:为了方便阅读,也为了防止隔天就忘了自己写的代码是什么意思,用来说明的的文字就叫做注释,注释不会被编译,Java编译器会自动略过注释语句。
  • 单行注释:使用 // 开头,// 所在行后面的所有语句均为注释。
    //这是单行注释
  • 多行注释:使用 /* 开头以 */ 结尾,/* 和 */ 中间的内容都是注释,可以多行。
    /*这是多行注释
    这是多行注释
    这是多行注释*/
  • 文档注释:使用 /** 开头以 */ 结尾,/** 和 */ 中间的内容都是注释,注释中包含一些说明性的文字及一些JavaDoc标签(后期写项目时,可以直接生成项目的API)
    /**这是文档注释
     * 这是文档注释
     * 这是文档注释*/

标识符

  • 标识符的作用:标识符是用来给变量、常量、类、方法以及包进行命名。
  • 标识符规则:
    • 标识符必须以“Unicode字母”、“下划线_”或“美元符号$”开头。
    • 标识符其他部分可以是“Unicode字母”、“下划线_”、“美元符号$”和“数字”的组合。
    • Java对标识符大小写敏感,长度无限制。
    • 标识符不可是Java关键字,如“class”、“package”、“new”等。
  • 标识符的使用规范:
    • 表示类名单词首字母大写,如“Man”、“AaronHzy”。
    • 表示方法和变量的标识符,采用“驼峰原则”:首单词小写,第二个单词开始首字母大写,如“eatFood()”、"takeYourHeart()"、“int zheShiBianLiang”。
    • 表示常量,大写字母和下划线组合,如“MAX_VALUE”、"TEST_CONSTANT"。
      Java采用的是Unicode字符集,只要是Unicode支持的字符都可以作为标识符,也就是说不仅仅支持英文,中文也是支持的。
  • 标识符演示:
    //合法的标识符
    int Test = 233;
    int _123 = 233;
    int $12a = 233;
    int 变量1 = 233;
    
    //不合法的标识符
    int 1a = 233; //不能使用数字开头
    int a# = 233; //不能包含#这样的特殊符号
    int int = 233; //不能使用Java关键字

Java关键字(保留字)

关键字含义
abstract表明类或者成员方法具有抽象属性
assert断言,用来进行程序调试
boolean基本数据类型之一,布尔类型
break提前跳出一个块
byte基本数据类型之一,字节类型
case用在switch语句之中,表示其中的一个分支
catch用在异常处理中,用来捕捉异常
char基本数据类型之一,字符类型
class声明一个类
continue回到一个块的开始处
default默认,例如,用在switch语句中,表明一个默认的分支
do用在do-while循环结构中
double基本数据类型之一,双精度浮点数类型
else用在条件语句中,表明当条件不成立时的分支
enum枚举
extends表明一个类型是另一个类型的子类型,这里常见的类型有类和接口
final用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量
finally用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
float基本数据类型之一,单精度浮点数类型
for一种循环结构的引导词
if条件语句的引导词
implements表明一个类实现了给定的接口
import表明要访问指定的类或包
instanceof用来测试一个对象是否是指定类型的实例对象
int基本数据类型之一,整数类型
interface接口
long基本数据类型之一,长整数类型
native用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new用来创建新实例对象
package
private一种访问控制方式:私用模式
protected一种访问控制方式:保护模式
public一种访问控制方式:共用模式
return从成员方法中返回数据
short基本数据类型之一,短整数类型
static表明具有静态属性
strictfp用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 [1] 
super表明当前对象的父类型的引用或者父类型的构造方法
switch分支语句结构的引导词
synchronized表明一段代码需要同步执行
this指向当前实例对象的引用
throw抛出一个异常
throws声明在当前定义的成员方法中所有需要抛出的异常
transient声明不用序列化的成员域
try尝试一个可能抛出异常的程序块
void声明当前成员方法没有返回值
volatile表明两个或者多个变量必须同步地发生变化
while用在循环结构中
goto保留关键字,在C/C++中可以实现程序的跳转,但会降低可读性,所以Java将其禁用
const保留关键字,与goto类似
  • this关键字:本质代表“创建好的对象的地址”,由于在构造方法调用前对象已创建,因此在构造方法中可以使用this代表“当前对象的地址”,this常用的用法:
    • 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象
    • 使用this关键字调用重载的构造方法,可避免相同的初始化代码,但只能在构造方法中用,并且必须位于构造方法的第一句
    • this不能用于static方法中
      public class Test{
        int a, b, c;
        
        test(int a, int b){
          // 当变量发生二义性,可使用this来区分局部变量和成员变量
          this.a = a; //当前构造方法的a=成员变量a
          this.b = b;
        }
        
        test(int a, int b, int c){
          // 构造函数调用必须是构造函数中的第一个语句
          this(a,b); //直接调用,在构造器中调用另一个构造器,需要使用this
          this.c = c;
        }
        
        void sing(){
          System.out.println("我是sing方法,我被调用啦!");
        }
        
        void eat() {
          this.sing(); //调用本类中的sing();方法
          System.out.println("你妈喊你回家吃饭!");
        }
        public static void main(String[] args) {
          test hi = new test(2, 3);
          hi.eat();
        }
      }
  • static关键字:在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:
    • 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化
    • 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享
    • 一般用“类名.类属性/方法”来调用(也可以通过对象引用或类名(不需要实例化)访问静态成员)
    • 在static方法中不可直接访问非static的成员
      static修饰的成员变量和方法,从属于类;普通变量和方法从属于对象的。public class Test{
        int id;
        String name,pwd;
        static String company = "测试,我被调用啦"; //赋值静态成员变量company
        
        public static void main(String[] args) {
          test t = new test(001, "黄泽雨"); //创建对象并赋值
          test.printCompany(); //调用printCompany();方法
          
          test.company = "测试信息2"; //修改变量company
          test.printCompany(); //重新打印
          System.out.println(t);
        }
        
        // 构造方法
        public test(int id, String name) {
          this.id = id; //当前构造方法的id=成员变量id
          this.name = name;
        }
        
        public void login() {
          System.out.println("登录账号:"+name);
        }
        
        public static void printCompany() {
          System.out.println(company); //输出静态成员变量company
        }
      }
    • static静态初始化块:构造方法用于对象的初始化;静态初始化块,用于类的初始化;在静态初始化块中不能直接访问非static成员,注意事项:
    • 上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止
    • 构造方法执行顺序和上述顺序一样
      public class Test{
        int id;
        String name, pwd;
        static String company;
        
        public static void main(String[] args) {
          test  t = new test();
        }
        
        // 用static修饰的静态块,类初始化时执行
        static {
          System.out.println("执行类的初始化工作");
          company = "测试内容"; //给静态变量company赋值
          printCompany(); //执行printCompany方法
        }
        
        public static void printCompany(){
          System.out.println(company);
        }
      }

    数据类型与运算符 - 变量、常量、数据类型

    变量(Variables)

    • 变量的本质:变量代表一个“可操作的存储空间”,空间位置、类型、大小是确定的,但空间内的值是不确定的,可以通过变量名访问“对应的储存空间”,从而操作该空间的值。
      如果把“可操作的存储空间”比作“车位”,“储存空间里的数据”比作“不同品牌的车”,那“变量名”就是“车位号”,我们可以直接通过“车位号”找到这个“车位”,“车位”里停什么类型的车、车的大小、车位的位置是固定的,但车位里停什么品牌的车是随机的。
    • 变量的声明:Java是一种强类型语言,每个变量都必须声明其数据类型,变量的数据类型决定了变量占据存储空间的大小(详见下方数据类型)。
      变量作为程序中最基本的存储单元,其要素包括数据类型,变量名和作用域,变量在使用前必须对其“声明”,只有在变量声明以后,才能为其分配相应长度的存储空间。//声明变量格式,声明语句以分号结束
      type varName [=value][,varName[=value]...];
      //[]内容是可选项
      数据类型 变量名 [=初始值] [,变量名 [=初始值]…];
      
      //声明变量例子
      double test; //double类型变量 test
      long earthPopulation; //long类型变量 earthPopulation
      int age,age2; //同时声明2个int类型变量 age 和 age2
      int age=10, age2=12; //同时声明2个int类型变量 age 和 age2,并同时赋值
      int ageTest = 18; //声明int类型的变量 ageTest 的同时初始化值(赋值),不赋值则使用默认值
    • 变量的分类:
      变量类型声明位置从属于生命周期
      局部变量(Local variables)方法内部语句块内部方法语句块从声明位置开始,直到方法或语句块执行完毕,局部变量消失
      成员变量(实例变量)(Member variables)类内部方法外部对象对象的成员,伴随对象始终,创建对象时实例化,对象消失时跟着消失
      静态变量(类变量)(Static variable)类内部static修饰类被加载,静态变量就有效;类被卸载,静态变量消失
    • 局部变量(Local variables)
      //  类Variables
      public class Variables{
        // 方法体 main
        public static void main(String[] args){
          int age = 10; //局部变量 age,在main方法中都可以使用
          System.out.println(age); //输出局部变量 age,该变量属于该方法体,只能在方法内调用
          // 语句块
          {
             String name = "AaronHzy"; //局部变量 name,在该语句块中都可以使用
             System.out.println(name); //输出局部变量 name,该变量属于该语句块,只能在语句块内调用
          }
        }
      }
    • 成员变量(实例变量 Member variables)
      // 类Variables
      public class Variables{
        int abc = 100; //成员变量,创建对象后都可以使用该变量
        // 方法体 main
        public static void main(String[] args){
          System.out.println(abc); //方法中输出成员变量 abc
          //语句块
          {
            System.out.println(abc); //语句块中输出成员变量 abc
          }
        }
      }
    • 静态变量(类变量 Static variable)
      // 类Variables
      public class Variables{
        static int test = 123; //静态变量,使用static修饰,仅能在类的内部使用
        // 方法体 main
        public static void main(String[] args){
          System.out.println(test); //方法中输出静态变量 test
          //语句块
          {
            System.out.println(test); //语句块中输出静态变量 test
          }
        }
      }

    常量(Constant)

    • 常量的本质:常量通常指的是一个固定的值,初始化(赋值)后不可被改变,通常利用关键字“final”来定义。
      // 声明常量格式,声明语句以分号结束
      final type varName = value;
      // []内容是可选项
      final 数据类型 变量名 = 初始值;
      // 声明常量例子
      final int age = 18; //double类型常量 age
      final String name = "AaronHzy"; //String类型常量 name

    数据类型(Primitive Data Type)

    • 数据类型作用:Java是一种强类型语言,每个变量都必须声明其数据类型。自学JAVA知识点总结 - 初级
    • 局基本数据类型(Primitive Data Type):Java内置的数据类型,拥有六种数值类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔类型。
      // 整数类型(精确的)
      byte a = 127;
      short b = 32767;
      int c = 2147483647;
      long d = 9223372036854775807L; //默认是int类型,要使用long类型需要在末尾加“L”转型
      
      int a = 15; //十进制
      int b = 015; //以0开头的是八进制
      int c = 0x15; //以0x开头的是16进制
      int d = 0b1101; //以0b或0B开头的是二进制
      
      // 浮点类型(不精确的,不可直接用于比较)
      double a1 = 3.14D; //十进制,可以在结尾添加“D”转型到double类型,也可以不加,默认double
      double a2 = 300e2; //科学计数法,e2表示10^2,这里就等于300*10^2=30000
      float a3 = 3.14F; //浮点常量默认是double类型,如要使用float类型必须在结尾加“F”
      long a4 = 9223372036854775807L; //默认是int类型,要使用long类型需要在末尾加“L”转型
      
      // 如果一定要将浮点数进行比较,可使用 java.math包下面的两个类:BigInteger和BigDecimal
      // 这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算
      BigDecimal bd = BigDecimal.valueOf(1.0); //设置bd值为0.1
      bd = bd.subtract(BigDecimal.valueOf(0.1)); //bd减去0.1
      bd = bd.subtract(BigDecimal.valueOf(0.1)); //bd减去0.1
      bd = bd.subtract(BigDecimal.valueOf(0.1)); //bd减去0.1
      bd = bd.subtract(BigDecimal.valueOf(0.1)); //bd减去0.1
      bd = bd.subtract(BigDecimal.valueOf(0.1)); //bd减去0.1
      System.out.println("BigInteger计算: " + bd); //输出BigInteger计算的数值
      System.out.println("直接计算: " + (1.0 - 0.1 - 0.1 - 0.1 - 0.1 - 0.1)); //输出直接计算得出的数值,查看差异
      
      // 字符型
      char b1 = 'T'; //可使用Unicode编码支持的字符
      char b2 = '中'; //可使用Unicode编码支持的字符
      char b3 = '\u0061'; //也可直接使用“Unicode编码”
      char b4 = '\\'; //也可直接使用“转义字符”
      
      // 布尔类型
      boolean man = true; //只能为true或false,表示“真”和“假”
      /* 例子,判断是否为man,如果变量man为true,则输出语句“是否为男性:true”
         否则,则输出语句“是否为男性:false” */
      if(man){
        System.out.println("是否为男性:true");
      }else{
        System.out.println("是否为男性:false");
      }
    • 引用数据类型(Reference Data Type):引用类型指向一个对象,类型有类、接口、数组等,所有引用类型的默认值都是“null”。

    数据类型与运算符 - 运算符

    运算符

    • 运算符作用:计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操作变量。自学JAVA知识点总结 - 初级
    • 一元运算符 - 运算规则:
      • ++(自增)--(自减):
        int a = 1;
        int b = a++; //a先赋值给b,再自增
        System.out.prinln("a=" + a + "\nb=" + b); //此时输出 a=2,b=1
        int a = 1;
        int b = ++a; //a先自增,再赋值给b
        System.out.prinln("a=" + a + "\nb=" + b); //此时输出 a=2,b=2
        
    • 二元运算符 - 运算规则:
      • 整数运算:
        • 如果两个操作数有一个为long,则结果也为long。
        • 没有long时,结果为int,即使操作数全为short,byte,结果也是int。
      • 浮点运算:
        • 如果两个操作数有一个为double,则结果为double。
        • 只有两个操作数都是float,则结果才为float。
      • 取模运算:
        • 其操作数一般是整数,也可以为浮点数,结果是“余数”,“余数”正负和取模运算符左边操作数相同,例如:7%3=1;-7%3=-1;7%-3=1。
      • instanceof运算符:
        • 其操作数左边是对象,右边是类;当对象是右边类或子类所创建的对象时,返回true;否则返回false。
          public class Test {
            public static void main(String[] args) {
              Person p = new Person();
              System.out.println(p instanceof Person); //p是Person的对象,返回true
            }
          }
          class Person{}
    • 赋值运算符 - 运算规则:
      • 将赋值运算符右边的值赋给左边。
        int a = 3; //将3赋给变量a
        int b = a; //将a赋给变量b
        System.out.println("a=" + a + "\nb=" + b) //输出后 a=3,b=3
    • 扩展运算符 - 运算规则:
      • 将扩展运算符左边与右边进行 + - * / % 后赋值给左边。
        int a = 1;
        int b = 2;
        b+=a; //这里相当于 b=b+a,赋值后b=3
        b-=a; //这里相当于 b=b-a
        b*=a+3; //这里相当于 b=b*(a+3),先算a+3再算b*a,赋值后b=8
    • 关系运算符 - 运算规则:
      • 关系运算符用来进行比较运算,运算结果只有true和false。
        int a = 3;
        System.out.println(a==3); //这里会判断a是否等于3,如果是输出true
        System.out.println(a!=5); //这里会判断a是否不等于5,如果是输出true
        char b = 'a';
        char b2 = 'c';
        System.out.println(b<b2); //在这里char会被转为int类型来进行运算
        System.out.println((int)b); //可以利用强制转型查看对应的int数值
        ==、!= 是所有(基本和引用)数据类型都可以使用
        >、>=、<、<= 仅针对数值类型 byte/short/int/long、float/double、char
    • 逻辑运算符 - 运算规则:
      • 逻辑运算符用来对比布尔值(boolean),计算结果也是布尔值(ture/false)。
        boolean a = true;
        boolean b = false;
        System.out.println(a&b); //a和b有一个为false,则输出false
        System.out.println(a|b); //a和b有一个为true,则输出true
        System.out.println(!b); //单个值运算,取反,b为true输出false,b为false输出true
        System.out.println(a^b); //按断a和b是否相同,相同输出false,不同输出true
        System.out.println(1<3&&1>3); //为false,则输出false,从左到右计算,左边符合直接输出不计算右边,不符合则继续往后计算
        System.out.println(1<3||1>3); //为true,则输出true,从左到右计算,左边符合直接输出不计算右边,不符合则继续往后计算
    • 位运算符 - 运算规则:
      • 位运算指的是将值转为二进制进行二进制运算。
      • 运算符 &(与) 和 |(或) 既是逻辑运算符,也是位运算符,如果两侧操作数都是布尔类型,就作为逻辑运算符;如果两侧的操作数是整数类型,就是位运算符。
        int a = 3; //数值3转为二进制是0011
        int b = 4; //数值4转为二进制是0100
        System.out.println(a&b); //将整数类型3和4转为二进制进行运算
      • 「按位与」两个操作数必须都为true,才能为true,否则为false
      • 自学JAVA知识点总结 - 初级

      • 「按位或」两个操作数只要有一个是true,结果就是true
      • 自学JAVA知识点总结 - 初级

      • 「按位异或」两个操作数相同为false,不同为true
      • 自学JAVA知识点总结 - 初级

      • 左移运算符与右移运算符分别代表乘2和除2。
        int a = 100;
        System.out.println(a<<2); //位移2位,乘两次2,结果等于400 = (100*2)*2
        System.out.println(a>>2); //位移2位,乘两次2,结果等于25 = (100/2)/2
        
    • 字符串连接符 - 运算规则:
      • 运算符“+”两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接,此处“+”不作为算数运算符,而作为字符串连接符进行运算。
        int a = 12;
        System.out.prinln("a=" + a); //输出结果:a=12
    • 条件运算符 - 运算规则:
      • 条件运算符格式“x:y:z”,其中 x 为 boolean 类型表达式,先计算 x 的值,若为true,则运算结果为y,否则结果为 z 。
        int score = 80; //定义一个值
        String type  = score<60?"不及格":"及格"; //值score的值是否小于60,为true输出“不及格”,为false输出及格。
        System.out.prinln("最终成绩:" + type);
    • 运算符的优先级:
      • 建议使用括号运算符来组织表达式。
      • 切记,逻辑与/或/非的优先级是逻辑非>逻辑与>逻辑或,如:a||b&&c的运算结果是:a||(b&&c),而不是(a||b)&&c。
        序号运算符说明顺序
        1()括号运算符由左至右
        2!、+(正号)、-(负号)一元运算符由左至右
        2~位逻辑运算符由右至左
        2++、--递增与递减运算符由右至左
        3*、/、%算术运算符由左至右
        4+、-算术运算符由左至右
        5<<、>>位左移、右移运算符由左至右
        6>、>=、<、<=关系运算符由左至右
        7==、!=关系运算符由左至右
        8&位运算符、逻辑运算符由左至右
        9^位运算符、逻辑运算符由左至右
        10|位运算符、逻辑运算符由左至右
        11&&逻辑运算符由左至右
        12||逻辑运算符由左至右
        13? :条件运算符由右至左
        14=、+=、-=、*=、/=、%=赋值运算符、扩展运算符由右至左

    数据类型与运算符 - 数据类型转换

    数据类型转换

    常用类:待完善


    控制语句 - 语句块、方法体、修饰符

    语句块

    • 语句块:语句块(有时叫做复合语句),是用{}扩起来的简单Java语句,确定了局部变量的作用域;语句块中的语句是一个整体,会被一起执行;语句块可以嵌套在另一个语句块中,但不能在两个嵌套的语句块内声明同名的变量;语句块可以使用外部定义的变量,但外部无法使用语句块中定义的变量,因为语句块中定义的变量作用于只限于语句块内。
      public class Test{
        public static void main(String[] args){
          int a;
          int b;
          {
            int a; //编译错误,不可在嵌套语句块中重复定义变量a
            int c; //语句块中的语句
          } //变量c作用于到此为止
        }
      }

    方法体

    • 方法体:方法就是一段用来完成特定功能的代码片段,类似于其它语言的函数;用于定义该类(class)实例(对象)的行为特征和功能实现,可反复调用方法,执行方法内的语句。
      面向对象中,整个程序的基本单位是类(class),方法是从属于类和对象的。// 方法声明格式
      [修饰符1 修饰符2 ...] 返回值类型 方法名(形式参数列表){
        java语句;
      }
      
      // 方法例子
      public class test {
        public static void main(String[] args) { //程序的入口,main方法,固定格式
          Animal c = new Animal(); //创建对象
          int b = c.dog(10, 20, 30)+1000; //实参,必须为int类型
          System.out.println(b);
        }
      }
      class Animal{
        int dog(int a, int b, int c) { //int类型的a,b,c是形式参数,没有具体的数据
          int sum = a+b+c; //当该方法被调用并将实参传递过来后,进行计算
          System.out.println("狗的数量:" + sum);
          return sum; //return作用:1.结束方法的运行 2.返回值
          System.out.println("这段不会返回"); //因为上面有return,这段代码将不会执行
        }
      }
      
    • 详细说明:
      • 形式参数:又称虚拟变量,在方法声明时用于接受外界传入的数据,没有具体的数据。
      • 实际参数:调用方法时实际传给方法的数据。
      • 返回值:方法在执行完毕后返还给调用它的环境的数据。
      • 返回值类型:事先约定的返回值的数据类型,如无返回值,必须指定为void。
    • 注意事项:
      • 实参的数目、数据类型和次序必须和所调用方法,声明的形式参数列表匹配。
      • return 语句终止方法的运行并指定要返回的数据。
      • Java中在方法调用时传递参数,遵循值传递的原则(传递的都是数据的副本,不影响原数据,但都指向同一个对象)。
    • 方法重载(Overload):方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。只有形参类型、形参个数、形参顺序不同才能构成重载。
      返回值不同、参数名称不同,不构成方法的重载public class test {
        public static void main(String[] args) {
          dog(10, 20, 30); //对应方法一
          dog(10, 20); //对应方法二
          dog(10.5, 20.5); //对应方法三
        }
        
        // 方法一
        static void dog(int a, int b, int c) {
          int sum = a+b+c;
          System.out.println("狗的数量:" + sum);
        }
        
        //方法二
        static void dog(int a, int b) { //dog传递到这里时候会被重写为对应的值 int 10, 20
          int sum = a+b;
          System.out.println("狗的数量:" + sum);
        }
        
        //方法三
        static void dog(double a, double b) { //dog传递到这里时候会被重写为对应的值 double 10.5, 20.5
          double sum = a+b;
          System.out.println("狗的数量:" + sum);
        }
      }
    • 方法重写(Override):子类通过重写父类的方法,可以用自身的行为替换父类的行为。方法的重写是实现多态的必要条件。
      public class Test {
        public static void main(String[] args) {
          Horse h = new Horse();
          h.run(); //输出重写后的父类方法
        }
      }
      class Vehicle{
        public void run() {
          System.out.println("跑!");
        }
        public void stop() {
          System.out.println("停!");
        }
      }
      
      class Horse extends Vehicle{
        // 重写父类run方法
        public void run() {
          System.out.println("马奔跑,嘚嘚嘚嘚....");
        }
      }
      • 方法名、形参列表相同
      • 返回值类型和声明异常类型,子类小于等于父类
      • 访问权限,子类大于等于父类

    修饰符

    • 访问修饰符:Java 支持 4 种不同的访问权限,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。
    • 访问控制权限
      修饰符当前类同包内同包子类不同包子类其他包
      public(公共)
      protected(保护)
      default(缺省)
      private(私有)
    • 可用范围
      修饰符类(class)接口(interface)方法(method)变量(variables)
      public(公共)
      default(缺省)
      protected(保护)
      private(私有)
      • default(缺省):即留空,不使用任何修饰符,在同包内可访问。
      • public(公共):在同类内可访问。注意:不能修饰外部类
      • private(私有):所属类可访问。
      • protected(保护):对同包内的类和所有子类可访问。注意:不能修饰外部类。
      • package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
              Test t = new Test();
              t.pr = 123; //同类可调用private修饰的变量
          }
          
          private int pr; //私有变量pr
          int def; //缺省使用默认权限
          protected int pro;
          public int pu;
        }
        class boy extends Test{
          void sayPr2() {
            //pr = 123; //不可用,因为pr变量是私有变量,不同类无法调用,即使是继承也不可用
            def = 123; //可用,因为def是defalut修饰,同包内的类都可以调用
            pro = 123; //可用,因为pro是protectied修饰,同包内的类都可以调用
            pu = 123; //可用,因为pu是public修饰,同包内的类都可以调用
          }
        }
         
        ================================= 分割线 =================================
        
        package me.hzyis.test2;
        import me.hzyis.test.*;
        public class Test2 {
          public static void main(String[] args) {
            Test t = new Test();
          }
        }
        class boy extends Test{
          void sayPr2() {
            //pr = 123; //不可用,因为pr变量是私有变量,不同类无法调用
            //def = 123; //不可用,因为def是default修饰,不同包的类无法调用
            pro = 123; //可用,因为pro是protectied修饰,不同包内的子类都可以调用
            pu = 123; //可用,因为pu是public修饰,不同包内的子类都可以调用
          }
        }
    • 非访问修饰符:为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
      • static(静态):用来修饰类方法和类变量。
        • 静态变量:声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝;静态变量也被称为类变量;局部变量不能被声明为 static 变量。
        • 静态方法:声明独立于对象的静态方法;静态方法不能使用类的非静态变量;静态方法从参数列表得到数据,然后计算这些数据。
      • final(最终):变量一旦赋值后,不能被重新赋值,成为常量。
        final class test{ //类不能被继承,类似Math、String等
          final void study(){ //方法无法被子类重写,但可以被重载
            final int A = 10; //int变量a无法被重新赋值,成为常量。
            A = 20; //报错,无法被重新赋值
          }
        }
        
      • abstract(抽象):变量一旦赋值后,不能被重新赋值,成为常量。
        • 抽象类:不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。一个类不能同时被 abstract 和 final 修饰
        • 抽象方法:一种没有任何实现的方法,该方法的的具体实现由子类提供。
          抽象方法不能被声明成 final 和 staticpublic abstract class SuperClass{
            abstract void m(); //抽象方法
          }
           
          class SubClass extends SuperClass{
            void m(){
              System.out.println("test");
            }
          }
      • synchronized(同步):同一时间只能被一个线程访问。
      • transient(过渡):序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。 该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
      • volatile(易失):volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

    控制语句 - 选择结构

    if语句

    • if单选择结构:对布尔表达式进行一次判定,若判定为true,则执行 {} 中的语句块,否则跳过该语句块。
      public static void main(String[] args) {
        int a = (int) (6*Math.random())+1; //通过函数Math.random()产生一个随机数
          if (a<=3){ //如果a<=3为true,则输出语句块中的语句
            System.out.println("手气不太好啊,分数只有: " + a); //如果如果a<=3为true,则输出这条语句
          }
          if (a>3 && a<=5){ //如果a>3 && a<=5为true,则输出语句块中的语句
            System.out.println("手气不错,获得分数: " + a); //如果如果a>3 && a<=5为true,则输出这条语句
          }
          if (a==6) { //如果a==6,则输出语句块中的语句
            System.out.println("爆炸人品,获得分数: " + a); //如果如果a==6,则输出这条语句
          }
      }
    • if双选择结构:当布尔表达式为true时执行语句块1,否则执行语句块2(也就是else部分)。
      public static void main(String[] args) {
        double r = 4 * Math.random(); // 随机产生[0.0 , 4.0]区间的半径,并根据半径求圆的面积和周长
        // Math.pow(r , 2) 求半径r的二次方
        // Math.Pi 指的是圆周率
        double area = Math.PI * Math.pow(r , 2); // S=π*r^2 求圆的面积
        double circle = 2 * Math.PI * r; // C=2*πr 求圆的周长
        System.out.println("半径为:" + r);
        System.out.println("面积为:" + area);
        System.out.println("周长为:" + circle);
        if (area <= circle) { //如果area <= circle为true,则输出语句块中的语句
          System.out.println("面积小于等于周长"); //如果如果area <= circle为true,则输出这条语句
        }else { //否则
          System.out.println("面积大于周长"); //否则输出这条语句
        }
      }
    • if多选择结构:当布尔表达式1为true时执行语句块1;否则判断布尔表达式2(else if),当布尔表达式2为true时执行语句块2;否则判断布尔表达式3(else if),当布尔表达式3为true时执行语句块3,否则……;如果1~n个布尔表达式均判定为false时,则执行语句块n+1,也就是else部分。
      public static void main(String[] args) {
        int score = (int)(100 * Math.random()); //生成一个0-100的随机数,定义分数
        System.out.println("成绩: " + score + "分");
        if (score < 60) { //如果分数< 60为true,则输出不及格,否则判断下一个else if
          System.out.println("不及格,再接再厉");
        }else if (score < 90) { //如果分数<90为true,则输出及格,否则判断下一个else if
          System.out.println("及格,继续努力");
        }else if (score < 100) { //如果分数<100为true,则输出好成绩,否则跳过该语句块,执行下面的语句
          System.out.println("好成绩,继续保持");
        }
      }

    switch语句

    • switch多选择结构:switch语句会根据表达式的值从相匹配的case标签处开始执行,一直执行到break语句处或者是switch语句的末尾。如果表达式的值与任何case值不匹配,则进入default语句(如果存在default语句的情况),如果default不存在则跳过,执行下面的语句。
      switch语句中case标签在JDK1.5之前必须是整数(long类型除外)或者枚举,不能是字符串,在JDK1.7之后允许使用字符串(String)。
      当布尔表达式是多值判断,可以使用if多选择结构或者switch结构,值过多建议switch结构(更简洁);如果布尔表达式是区间判断,则只能使用if多选择结构。
      // switch语句结构 switch (表达式){ case 值1:{ 语句序列; [break]; //可选,执行到这里结束switch } case 值2:case 值3:{ //匹配到值2或3都会执行该语句块内的语句 语句序列; [break]; } default:{ 语句序列; [break]; } } // 例子 public static void main(String[] args) { int month = (int)(1+12*Math.random()); //1-12的随机数代表月份 System.out.println("输出的月份:" + month); // 由case开始,到break结束 switch(month) { //switch判断month case 1:{ //如果month为1,则输出一月份 System.out.println("当前月份:" + "一月份"); break; //执行到这里结束 } case 2:case 3:{ System.out.println("当前月份:" + "二到三月份"); break; } case 4:case 5:{ System.out.println("当前月份:" + "四到五月份"); break; } case 6:case 7:{ System.out.println("当前月份:" + "六到七月份"); break; } default:{ //如果以上case没有符合的值,则输出default内的语句块 System.out.println("当前月份:" + "其他月份"); break; } } }

    控制语句 - 循环结构

    while语句

    • while循环结构:当类循环,先判断后执行,当符合条件则执行循环,如果不符合条件则跳出循环。循环语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。
      // while格式
      while(布尔表达式){ //当满足条件执行循环
        循环语句;
      }
      
      // 求 1+2+3+4+5...+100 的和,使用暴力算法累加计算
      public class test {
        public static void main(String[] args) {
          int a = 1; //开始的值
          int sum = 0; //sum变量代表和
          while(a<=100) { //判断a<=100是否为true,符合执行循环
            sum = sum+a; //计算和,第一次sum=0+1; 第二次sum=1+2; 第三次sum=3+3.....加到100后求最终和
            a++; //a自增1,用来计数
          }
          System.out.println(sum);
        }
      }
        
      // 解读循环步骤
          //第一次循环
          while(a<=100) { //此时a=1,符合条件继续循环
            sum = sum+a; //此时sum=0 加上 a=1
            a++;
          }
          // 第二次循环
          while(a<=100) { //此时a=2,符合条件继续循环
            sum = sum+a; //此时sum=1 加上 a=2
            a++; //此时a=2然后自增为3
          }
          //第三次循环
          while(a<=100) { //此时a=3,符合条件继续循环
            sum = sum+a; //此时sum=3 加上 a=3
            a++; //此时a=3然后自增为4
          }
          //第四次循环
          while(a<=100) { //此时a=4,符合条件继续循环
            sum = sum+a; //此时sum=6 加上 a=4
            a++; //此时a=4然后自增为5
          }
    • do-while循环结构:当类循环,先执行后判断,先执行一次循环,当符合条件则继续执行循环,如果不符合条件则跳出循环。
      // do-while格式格式
      do{ //先执行一次循环
        循环语句;
      }while(布尔表达式); //再判断,符合条件则继续循环,否则跳出循环
      
      // 求 1+2+3+4+5...+100 的和,使用暴力算法累加计算
      public class test {
        public static void main(String[] args) {
          int a = 1; //开始的值
          int sum = 0; //sum变量代表和
          do { //先执行一次
            sum += a; //sum = sum+a
            a++;
          } while (a<=100); //再判断
          System.out.println(sum);
        }
      }
      

    for语句

    • for循环结构:for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。for循环在第一次反复之前要进行初始化,即执行初始表达式;随后,对布尔表达式进行判定,若判定结果为true,则执行循环,否则终止循环;最后在每一次反复的时候,进行某种形式的“步进”,即执行迭代因子。
      // for循环格式
      for (初始化表达式,初始化表达式2; 布尔表达式; 迭代因子,迭代因子2){
        循环语句;
      }
       
      // 求 1+2+3+4+5...+100 的和,使用暴力算法累加计算
      public class test {
        public static void main(String[] args) {
          int sum = 0; //sum变量代表和
          // 先执行初始化表达式int a=1;执行后进行判断a<=100;判断后进行循环;循环后迭代因子自增计数
          for(int a=1; a<=100; a++) {
            sum += a; //计算和
          }
          System.out.println(a); // 报错,for循环中的变量,只能在for循环中使用
          System.out.println(sum);
        }
      }

    嵌套循环

    • while、do-while、for嵌套循环:在一个循环语句内部再嵌套一个或多个循环,称为嵌套循环。while、do-while与for循环可以任意嵌套多层。
      // 输出99乘法表
      public class test {
        public static void main(String[] args) {
          for(int a=1; a<=9; a++) {
            for(int b=1; b<=a; b++) {
              System.out.print(b + "*" + a + "=" + b*a + "\t");
          }
          System.out.println();
          }
        }
      }
       
      // 解读循环步骤
          // 第一层for,第一次循环
          for(int a=1; a<=9; a++) { //a=1,a<=9符合条件,进行循环
            // 第二层for,第一次循环
            for(int b=1; b<=a; b++) { //b=1,(b=1)<=(a=1)符合条件,进行循环
              System.out.print(b + "*" + a + "=" + b*a + "\t"); //输出1*1=1+\t制表符
            } //b自增
            // 第二层for,第二次循环
            for(int b=1; b<=a; b++) { //b=2,(b=2)<=(a=1)不符合条件,跳出循环
              System.out.print(b + "*" + a + "=" + b*a + "\t"); //不执行
            } //跳出循环,b=1
            System.out.println(); //输出空行
          }
          
          // 第一层for,第二次循环
          for(int a=1; a<=9; a++) { //a=2,a<=9符合条件,进行循环
            // 第二层for,第一次循环
            for(int b=1; b<=a; b++) { //b=1,(b=1)<=(a=2)符合条件,进行循环
              System.out.print(b + "*" + a + "=" + b*a + "\t"); //输出1*2=2+\t制表符
            } //b自增
            // 第二层for,第二次循环
            for(int b=1; b<=a; b++) { //b=2,(b=2)<=(a=2)符合条件,进行循环
              System.out.print(b + "*" + a + "=" + b*a + "\t"); //输出2*2=4+\t制表符
            } //b自增
            // 第二层for,第三次循环
            for(int b=1; b<=a; b++) { //b=3,(b=3)<=(a=2)不符合条件,跳出循环
              System.out.print(b + "*" + a + "=" + b*a + "\t"); //不执行
            } //跳出循环,b=1
            System.out.println(); //输出空行
          }
      

    break和continue语句

    • break语句:在任何循环语句的主体部分,均可用break控制循环的流程,break用于强行退出循环,不执行循环中剩余的语句。
    • continue语句:continue用在while,do-while中,continue 语句立刻跳到循环首部,越过了当前循环的其余部分;在for循环中,跳到for循环的迭代因子部分。
      public class test {
        public static void main(String[] args) {
          int count = 0; //定义计数器
          for (int i = 100; i < 150; i++) {
            // 如果是3的倍数,则跳过本次循环,继续进行下一次循环
            if (i % 3 == 0){
              continue; //跳过本次循环,继续进行下一次循环
            }
            // 否则(不是3的倍数),输出该数
            System.out.print(i + "\t");
            count++; //没输出一个数,计数器加1
            // 根据计数器判断每行是否已经输出了10个数
            if (count % 10 == 0) {
            	break; //输出达到10个数结束循环
            }
          }
        }
      }
    • 带标签的break语句:类似goto,强行退出循环,不执行循环中剩余的语句。
    • 带标签的continue语句:类似goto,跳过本次循环,跳到指定标签位置。
      public class test {
          public static void main(String args[]) {
              // 打印101-150之间所有的质数
          	outer: for (int i = 101; i < 150; i++) {
                  for (int j = 2; j < i / 2; j++) {
                      if (i % j == 0){
                          continue outer; //跳过本次循环,跳到outer标签位置
                      }
                  }
                  System.out.print(i + "  ");
              }
          }
      }

    控制语句 - 递归结构

    递归结构

    • 递归:递归是一种常见的解决问题的方法,把问题逐渐简单化,递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己;利用递归可以用简单的程序来解决一些复杂的问题。比如:斐波那契数列的计算、汉诺塔、快排等问题。
      • 定义递归头。什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。
      • 递归体。什么时候需要调用自身方法。简单的程序是递归的优点之一,但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。
    • 递归结构:
      // 递归:计算5!(阶乘)(5*4*3*2*1)
      public class test {
        public static void main(String[] args) {
          // 计算递归,%d是占位符返回第二位值10,%s返回第三位值factorial(5),%n是换行符
          System.out.printf("%d阶乘的结果:%s%n", 5, factorial(5));
        }
        // 求阶乘的方法
        static long factorial(int n){
          if(n==1){ //递归头
            return 1;
          }else{ //递归体
            return n*factorial(n-1); //结束运行并返回 n! = n * (n-1)!
          }
        }
      }
      

      自学JAVA知识点总结 - 初级


    面向对象 - 了解面向对象

    面向过程与面向对象的区别

    • 面向过程:面向过程是按步骤实现,并将步骤对应成方法,一步一步最终完成,适合简单任务,不需要过多协作的情况,例如“如何开车”:1.系安全带 2.启动发动机 3.挂挡 4.踩油门 5.走。
    • 面向对象:面向对象是先思考“如何设计这个事物”,先规划好大的框架,再完善框架内的东西,例如“如何造车?”:1.车架 2.发动机 3.座椅 4.挡风玻璃 5.轮胎 6.内饰。更适用于协作完成大型工程。
    • 相辅相成:面向过程和面向对象是相辅相成的,都是解决问题的思维方式,也都是代码组织的方式,宏观上使用面向对象把握大局,细微处理仍然是面向过程。例如造车,每一个车的部件都是一个对象,将这些对象拼装在一起就造成了一台车,但这些对象(部件)需要详细的制作流程,这些制作流程就是面向过程。

    数据管理与企业管理共通之处

    • 数据无管理:当数据量非常少,例如只有几个、十几个变量的时候,非常简单无需细致的管理;就像企业初期只有2、3个人,不需要很细致的管理模式,闷头做事就行了。
    • 数组管理:当数据量较大时,可以使用数组等方式分类管理,否则会很乱;就像一些中小公司部门制,会计部、销售部、技术部等。
    • 对象管理:当数据量特别大的时候,就需要使用对象来进行管理;就像大型企业,按项目来管理,例如支付宝项目组、淘宝项目组,这些项目组在细分为技术部门、宣传部门、会计部门等。

    对象和类的概念

    • 对象(Object):对象也可以叫实例(instance)、对象是具体的事物会在内存中生成。
    • 类(Class):类是对对象的抽象,类是一种抽象的数据类型,类是对象的模板,系统根据类的定义生成对象;例如造车:类就是图纸,对象就是车,类中的步骤信息就是语句/方法,根据类中的语句/方法,将对象造出来。
      抽象指的是:抽出像的部分,从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃;例如人怎么知道眼前的物体是辆车?1.见过被定义为车的物体 2.找出共同点(车架、车窗、轮子等特征) 3.见到一个物体识别特征,如果有车的特征那么这就是一辆车// 类的定义方式
      public class Test{ //每个源文件必须且只能有一个public类,并且类名与文件名保持一致
        public static void main(String[] args){ //main方法,程序入口,必须有
          test3 t = new test3(); //通过test3类创建test3对象
          t.testFF(); //调用test3类中的testFF方法
        }
      }
      // 一个java文件可以定义多个类(class)
      class test1{
      }
      class test2{
      }
      class test3{
        int a = 1; //一个类可以包含属性(成员变量)
        void testFF(){ //也可以包含一个方法或多个方法
          System.out.println("我是一个方法");
        }
        void testFF2(){
          System.out.println("我是方法2");
        }
      }

    对象创建的步骤详解

    • 创建对象:构造方法是创建对象的重要途径,通过new关键字调用构造器时,构造器也确实返回该类的对象,但这个对象并不是完全由构造器负责创建。创建一个对象分为如下四步:
      • 分配对象空间,并将对象成员变量初始化为默认值(布尔值是false、数值是0、引用类型为空)
      • 执行属性值的显式初始化
      • 执行构造方法(至此对象已创建,进行更进一步的初始化工作)
      • 返回对象的地址给相关的变量

    面向对象 - 内存分析

    JVM内存

    • 简介:Java虚拟机的内存可以分为三个区域:栈(stack)、堆(heap)、方法区(method area)。
    • 栈的特点:
      • 栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)。
      • JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)。
      • 栈属于线程私有,不能实现线程间的共享!
      • 栈的存储特性是“先进后出,后进先出”。
      • 栈是由系统自动分配,速度快,栈是一个连续的内存空间!
    • 堆的特点:
      • 堆用于存储创建好的对象和数组(数组也是对象)。
      • JVM只有一个堆,被所有线程共享。
      • 堆是一个不连续的内存空间,分配灵活,速度慢!
    • 方法区(静态区)的特点:
      • JVM只有一个方法区,被所有线程共享!
      • 方法区实际也是堆,只是用于存储类、常量相关的信息!
      • 用来存放程序中永远是不变或唯一的内容(类信息[Class对象]、静态变量、字符串常量等)。
        public class Test{
          int id,age; //定义int类的变量,学生id和年龄
          String name; //定义String类的变量,学生名称
          Computer comp; //定义Computer类的Comp变量
          
          void study() {
            System.out.println(age+"岁的"+name+"正在使用 " + comp.brand + " 型号计算机学习!他的ID是:"+id);
          }
          
          test(){} //系统自动创建,构造方法,用于创建类的对象,进行初始化
          
          public static void main(String[] args){
            test stu = new test(); //创建test对象stu
            Computer com = new Computer(); //创建Computer对象com
            System.out.println(stu); //获取test对象内存中的地址
            System.out.println(com); //获取Computer对象内存中的地址
            
            stu.id = 1;
            stu.age = 18;
            stu.name = "黄泽雨";
            com.brand = "AAA"; //定义Computer.brand的值
            stu.comp = com; //将Computer.brand赋值给Computer.comp
            stu.study(); //执行test类的方法study
          }
        }
        class Computer{
          String brand; //定义变量,电脑型号
        }

        自学JAVA知识点总结 - 初级


    面向对象 - 构造方法

    构造器

    • 构造器:构造器也叫构造方法(constructor),用于对象的初始化,构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化,构造器的名称应与类的名称一致,Java通过new关键字来调用构造器,从而返回该类的实例,是一种特殊的方法。
      编辑器会自动定义一个无参构造函数,如果已手动添加则编辑器不会自动添加,
      构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值。
      public class Test{ public static void main(String[] args){ Point p = new Point(3.0, 4.0); //创建Point类的对象,赋值传给Point的double _x,_y Point origin = new Point(0.0, 0.0); //再创建一个Point类的对象,赋值传给Point的double _x,_y System.out.println(p.getDistance(origin)); //计算距离,将origin赋值传给getDistance方法的p } } class Point{ double x,y; // 构造方法必须和类名保持一致 public Point(double _x,double _y) { x = _x; //_x赋值给x y = _y; //_y赋值给y } // 计算二维距离的方法 public double getDistance(Point p) { //这里的Point p用于传入origin的数据 System.out.println("x,y的值:" + x + "\t" + y); System.out.println("p.x,p.y的值:" + p.x + "\t" + p.y); // 这里的x,y是Point类的x,y;p.x, p.y是getDistance方法的x,y return Math.sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y)); //开方(乘方逆运算),(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)=2点距离 } }
    • 构造器的重载:构造方法也是方法,只不过有特殊的作用而已,与普通方法一样,构造方法也可以重载。
      public class Test{
        int id;
        String name;
        String pwd;
        public static void main(String[] args) {
          test u1 = new test(); //构造方法1
          test u2 = new test(101, "黄泽雨"); //构造方法2
          test u3 = new test(100, "老A", "123456"); //构造方法3
        }
        public test() {} //构造方法1
        public test(int id, String name) { //构造方法2,方法名称相同,形参列表不用,进行重载
          super();
          this.id = id;
          this.name = name;
        }
        public test(int id, String name, String pwd) { //构造方法3,方法名称相同,形参列表不用,进行重载
          this.id = id;
          this.name = name;
          this.pwd = pwd;
        }
      }

    面向对象 - Java机制与优化

    垃圾回收机制(Garbage Collection)

    • 垃圾回收过程:垃圾回收机制保证可以将“无用的对象”进行回收,无用的对象指的就是没有任何变量引用该对象,Java的垃圾回收器通过相关算法发现无用对象,并进行清除和整理。
    • 垃圾回收相关算法:
      • 引用计数法:堆中每个对象都有一个引用计数,被引用一次,计数加1。被引用变量值变为null,则计数减1,直到计数为0,则表示变成无用对象。优点是算法简单,缺点是“循环引用的无用对象”无法别识别。
        // 循环引用示例
        public class test {
          String name;
          Student friend;
          public static void main(String[] args){
            Student s1 = new Student();
            Student s2 = new Student();
            // 互相引用,导致他们引用计数不为0,但是实际已经无用,但无法被识别。
            s1.friend = s2;
            s2.friend = s1;
            s1 = null;
            s2 = null;
          }
        }
    • 引用可达法(根搜索算法):程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

    分代垃圾回收机制

    • 分代垃圾回收机制:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。将对象分为三种状态:年轻代、年老代、持久代。JVM将堆内存划分为 Eden、Survivor 和 Tenured/Old 空间。
      • 年轻代:年轻代分为1个Eden区和2个Survivor区,所有新生成的对象首先都是放在Eden区。 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象,对应的是Minor GC,每次 Minor GC 会清理年轻代的内存,算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域存放满对象后,就将对象存放到年老代区域。
      • 老年代:老年代分为1个Tenured(Old)区,在年轻代中经历了N(默认15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越多,就需要启动Major GC和Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。
      • 持久代:在方法区,用于存放静态文件,如Java类、方法等,对垃圾回收没有显著影响。
      • 自学JAVA知识点总结 - 初级

    • 垃圾回收过程:
      • 新创建的对象,绝大多数都会存储在Eden区
      • 当Eden区满了(达到一定比例)不能创建新对象,则触发Minor GC,将无用对象清理掉,然后剩余对象利用复制算法(coping)复制到某个Survivor区中,如S1(或S2,都可以),然后清空Eden区
      • 当Eden区再次满了,会将S1中的不能清空的对象存到另外一个Survivor中,例如S2,然后清空S1区;接着将Eden区中不能清空的对象,复制到Survivor区中,清空Eden区
      • 重复多次(默认15次)Survivor中没有被清理的对象,则会复制到老年代Tenured(Old)区中
      • 当老年代Tenured区达到一定比例,会触发Major GC;满了后则触发FullGC
    • 垃圾回收算法:
      • Minor GC:用于清理年轻代区域。Eden区满了就会触发一次Minor GC。清理无用对象,将有用对象复制到“Survivor1”、“Survivor2”区中(这两个区,大小空间也相同,同一时刻Survivor1和Survivor2只有一个在用,一个为空)。
      • Major GC:用于清理老年代区域。
      • Full GC:用于清理年轻代、年老代区域。 成本较高,会对系统性能产生较大影响。

    JVM调优

    • JVM调优:在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能调用Full GC:
      • 年老代(Tenured)被写满
      • 持久代(Perm)被写满
      • System.gc()被显式调用(程序建议GC启动,不是调用GC)
      • 上一次GC之后Heap的各域分配策略动态变化
    • 内存泄露(Memory Leak)常见原因:指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
      • 创建大量无用对象:例如需要大量拼接字符串时,使用了String而不是StringBuilder。
        String str = "";
        for (int i = 0; i < 10000; i++){
          str += i; //相当于产生了10000个String对象
        }
      • 静态集合类的使用:像HashMap、Vector、List等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放。
      • 各种连接对象:如IO流对象、数据库连接对象、网络连接对象等连接对象未关闭,这些连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。
      • 监听器的使用:释放对象时,没有删除相应的监听器。

    面向对象 - Java参数传值机制

    参数传值机制

    • 基本数据类型参数的传值:Java中,方法中所有参数都是“值传递”,传递的是值的副本。也就是说,得到的是“原参数的复印件,而不是原件”,因此,复印件改变不会影响原件。
    • 引用类型参数的传值:传递的是值的副本,但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址的对象的值”,意味着原参数指向对象的值也发生了改变。
      package test;
      
      public class Test{
        String name;
          
        public static void main(String[] args) {
          test  t  =  new test("黄泽雨"); //创建对象并赋值name
            
          t.test1(t); //调用test.test1方法,并将t传递给test1.u变量
          System.out.println(t.name); //输出name,在test1方法中,修改了name的值,原参数也会被改变,输出"黄泽雷"
          t.test2(t);
          System.out.println(t.name); //test2创建的新对象不影响当前对象中name的值,所以不发生变化,还是输出"黄泽雷"
        }
          
        public test(String name) {
          this.name = name;
        }
          
        public void test1(test u){ //从main传递来的 t 赋值给局部变量u
          u.name="黄泽雷"; //改变name的值,此时原参数name也发生了变化
        }
        
        public void test2(test u){ //从main传递来的 t 赋值给局部变量u
          u = new test("黄泽风"); //新建对象不影响上一个对象,所以上一个对象的name不发生变化
        }
      }

    面向对象 - Java包

    Java包(package)

    • java包:包机制是Java中管理类的重要手段,开发中会遇到大量同名的类,通过包可以很容易解决类重名的问题,也可以实现对类的有效管理,包对于类,相当于Windows中文件夹对于文件的作用。
      // package命名格式举例
      // 由于java的开源性,为了防止与他人包冲突,可以将域名倒着写,再加上项目名、模块名等易识别且不冲突d的命名方式
      // 例如我的域名 hzyis.me,开发一个java游戏项目test
      package me.hzyis.game.test;
        
      // package通常是类的第一句非注释性语句
      package me.hzyis.game.test;
      public class Test {
        public static void main(String[] args){
          System.out.println("测试包");
        }
      }
      me.hzyis和me.hzyis.game,这两个包没有包含关系,是两个完全独立的包,只是逻辑上看起来后者是前者的一部分。

    JDK常用包

    • jdk常用包:Jdk中本身包含了很多包,不同的包中有许多类,可以随时导入调用
      包名称说明
      java.ang包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能
      java.awt包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)
      java.net包含执行与网络相关的操作的类
      java.io包含能提供多种输入/输出功能的类
      javautil包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数

    导入类(import)

    • 导入类:如果我们要使用其他包的类,需要使用import导入,从而可以在本类中直接通过类名来调用,否则就需要书写类的完整包名和类名。import后,便于编写代码,提高可维护性。
      Java会默认导入java.lang包下所有的类,因此这些类可以直接使用。
      如果导入两个同名的类,只能用包名+类名来显示调用相关类:
      java.util.Date date = new java.util.Date(); // 导入其他包的类 package me.hzyis.test; import me.hzyis.test2.Test2; //导入me.hzyis.test2包中的Test2类 public class Test { public static void main(String[] args){ Test2 t = new Test2(); t.abc(); //调用me.hzyis.test2.Test2类中的方法abc } } ================================= 分割线 ================================= package me.hzyis.test2; // 导入java.util包中所有类,也可以使用import java.util.Scanner只导入Scanner import java.util.*; //导入所有类会降低编译速度 public class Test2 { public void abc() { Scanner s = new Scanner(System.in); System.out.println("请输入姓名:"); String name = s.nextLine(); System.out.println("你的姓名:"+name); } }
    • 静态导入(static import):静态导入是在JDK1.5新增加的功能,其作用是用于导入指定类的静态属性,这样可以直接使用静态属性。
      package me.hzyis.test;
      import static java.lang.Math.PI; //导入Math类的PI属性
      public class Test{
        public static void main(String [] args){
          System.out.println(PI);
        }
      }

    面向对象 - 继承、封装、多态、转型

    继承(Extends)

    • 面向对象继承:子类继承父类,子类是父类的扩展,例如你的财产、经验、知识可以继承给你的孩子;但不是所有类都可以继承,例如很私密的东西,就不能继承给你的孩子。继承实现了代码重用,不用再敲重复的代码了。
      public class Test {
        public static void main(String[] args) {
          // 由于Student继承了Person,只需创建Student对象就可以直接使用Person中的变量、方法
          Student stu = new Student();
          stu.name = "黄泽风";
          stu.age = 21;
          stu.rest();
          
          Student stu2 = new Student("黄泽雨",18,"山东南翔开挖掘机");
          
          System.out.println(stu.name + "\t" + stu.age);
          System.out.println(stu2.name + "\t" + stu2.age + "\t" + stu2.major);
        }
      }
       
      class Person{
        String name;
        int age;
        public void rest() {
          System.out.println("休息一会!");
        }
      }
       
      // 继承Person类
      class Student extends Person {
        String major;
        public void study() {
          System.out.println("学习2小时!");
        }
        // 构造器,可直接调用父类的变量
        public Student(String name, int age, String major) {
          this.name = name;
          this.age = age;
          this.major = major;
        }
        // 手动修改构造器后,IDE软件将不会自动添加空构造器
        public Student() {}
      }
      • 父类也称作超类、基类、派生类等。
      • Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
      • Java中类没有多继承,接口有多继承。
      • 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
      • 如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。在Eclipse中选中类按Ctrl+T可查看类的继承关系

    封装(Encapsulation)

    • 封装的作用:做一个产品要追求“高内聚,低耦合”,高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。封装可以提高代码安全性、复用性、可维护型、使用者便捷性。利用修饰符public(公共)、default(缺省)、protected(保护)、private(私有)来控制访问权限。
      package me.hzyis.test;
       
      public class Test {
        public static void main(String[] args) {
            Info i = new Info();
            i.setAge(-10);
            i.setName("黄泽雨");
            i.setFalg(true);
            System.out.println(i);
        }
      }
      class Info{
        private int age;
        private String name;
        private boolean falg;
        
        // 使用get或set方法代替直接修改私有变量
        // age的get和set方法
        public int getAge() {
          return age;
        }
        public void setAge(int age) {
          // 判断age是否合法
          if(age<0 || age>130) {
            this.age = 0; //不合法默认0
          }else {
            this.age = age; //合法才能赋值给属性age
          }
        }
        // name的get和set方法
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        // flag的is和set方法,boolean值的不是get是is
        public boolean isFalg() {
          return falg;
        }
        public void setFalg(boolean falg) {
          this.falg = falg;
        }
        @Override
        public String toString() {
          return "Person [name=" + name + ", age=" + age + "]";
        }
      }

    多态(Polymorphism)

    • java包:多态指的是同一个方法调用,由于对象不同可能会有不同的行为;多态与属性无关,指的是方法的多态;多态存在有3个必要条件:继承,方法重写,父类引用指向子类对象;父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
      package me.hzyis.test;
      
      public class Test {
        public static void main(String[] args) {
          Animal a = new Animal();
          animalCry(a);
          
          Dog d = new Dog();
          //多态,可避免过多重载方法
          animalCry(d); //子类对象给父类引用,也就是父类引用 指向 子类对象
          animalCry(new Cat());
        }
        static void animalCry(Animal a) { //传入Animal类的实例a
          a.shout(); //Animal类的实例a的方法shout
        }
      }
      class Animal {
        public void shout() {
          System.out.println("叫了一声!");
        }
      }
      class Dog extends Animal { //继承
        // 重写
        public void shout() {
          System.out.println("旺旺旺!");
        }
      }
      class Cat extends Animal {
        public void shout() {
          System.out.println("喵喵喵!");
        }
      }

    对象转型(Casting)

    • 对象转型:父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换;向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法,这时我们就需要进行类型的强制转换,我们称之为向下转型!
      package me.hzyis.test;
      
      public class Test {
        public static void main(String[] args) {
          Animal a = new Animal();
          animalCry(a);
          
          Dog d = new Dog(); //自动向上转型
          animalCry(d);
          animalCry(new Cat());
          
          Dog d2 = (Dog)d; //强制向下转型
          d2.seeDoor();
        }
        static void animalCry(Animal a) {
          a.shout();
        }
      }
      class Animal {
        public void shout() {
          System.out.println("叫了一声!");
        }
      }
      class Dog extends Animal {
        public void shout() {
          System.out.println("旺旺旺!");
        }
        public void seeDoor() {
          System.out.println("看门!!!");
        }
      }
      class Cat extends Animal {
        public void shout() {
          System.out.println("喵喵喵!");
        }
      }

    面向对象 - Object类常用方法

    Object类

    • Object类:Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法,如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
    • toString方法:Object类中定义有public String toString()方法,其返回值是 String 类型。Object类中toString方法的源码为:
      /* 默认会返回“<包>.<类名><@><16进制的hashcode>”
      在打印输出或者用字符串连接对象时,会自动调用该对象的toString()方法。*/
      public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
      }
       
      // 重写toString方法
      public class Test {
        int a = 123;
        public static void main(String[] args) {
          Test t = new Test();
          //打印对象,本质是调用toString方法,由于自动继承不需要写成 t.toString()
          System.out.println(t); 
        }
        
        public String toString() {
          return "重写Object类toString方法"+a;
        }
      }
      
    • ==和equals方法:“==”表示比较双方是否相同,如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象;“equals”在Object类中定义为public boolean equals(Object obj)方法,表示“对象内容相等”的逻辑,例如通过身份证判断是不是同一个人,再例如人脸识别把人脸生成一段数字用来判断是不是一个人,equals会比较两个对象的hashcode,是同一个对象的引用时返回true,否则返回false。
      // Object类中的equals方法
      public boolean equals(Object obj) {
        return (this == obj);
      }
       
      public class Test {
        public static void main(String[] args) {
          User u1 = new User(100,"黄泽雨","123456");
          User u2 = new User(100,"黄泽风","123456");
          
          System.out.println(u1 == u2); //为false,因为对象不同
          //重写equals后,只根据id判断,为ture
          System.out.println(u1.equals(u2)); //如果不重写,在Object默认还是使用==,由于对象不同则为false
        }
      }
      
      class User {
        int id;
        String name,pwd;
        
        public User(int id, String name, String pwd) {
          super();
          this.id = id;
          this.name = name;
          this.pwd = pwd;
        }
      
        /* 重写equals
          在Eclipse中Shift+Alt+S选择“生成hashCod()和equals()”,以id为判断依据,选择id即可自动生成*/
        @Override
        public boolean equals(Object obj) {
          if (this == obj) //如果传递的Object与obj相等,返回true
            return true;
          if (obj == null) //如果obj为null则返回false
            return false;
          if (getClass() != obj.getClass()) //如果类型不同则返回false
            return false;
          User other = (User) obj; //以上通过则强制转型为User
          if (id != other.id) //如果id不同则返回false
            return false;
          return true; //以上通过则返回true
        }
      }
      
    • super:super是直接父类对象的引用,可以通过super来访问父类中被子类覆盖的方法或属性;使用super调用普通方法,语句没有位置限制,可以在子类中随便调用;若是构造方法的第一行代码没有显式的调用super(...)或者this(...);那么Java默认都会调用super(),含义是调用父类的无参数构造方法,这里的super()可以省略。
      package me.hzyis.test;
        
      public class Test {
        public static void main(String[] args) {
          new ChildClass().f();
        }
      }
      class FatherClass {
        public int value;
        public void f(){
          value = 100;
          System.out.println ("FatherClass.value="+value);
        }
      }
      class ChildClass extends FatherClass {
        public int value;
        public void f() {
          super.f(); //调用父类对象的普通方法,不加super则调用本类的f()
          value = 200;
          System.out.println("ChildClass.value="+value);
          System.out.println("ChildClass.super.value="+super.value); //调用父类对象的成员变量
        }
      }
    • 继承树追溯:由于构造方法第一句总是super(...)来调用父类的对应的构造方法,所有构造方法调用顺序是向上追溯直到Object,然后再依次向下执行类的初始化块和构造方法,知道当前子类为止。属性/方法查找顺序是查找当前类,如果没有再向上查到父类,直到Object,如果还是没有则报错。
      package me.hzyis.test;
      
      public class Test {
        public static void main(String[] args) {
          System.out.println("开始创建一个ChildClass对象......");
          new ChildClass();
        }
      }
      class FatherClass {
        public FatherClass() {
          System.out.println("创建FatherClass");
        }
      }
      class ChildClass extends FatherClass {
        public ChildClass() {
          System.out.println("创建ChildClass");
        }
      }

    面向对象 - 抽象方法、抽象类、接口

    抽象方法、抽象类

    • 抽象方法:使用abstract修饰并包含在抽象类中的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
    • 抽象类:包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
      package me.hzyis.test;
      
      public abstract class Test { //抽象类
        // 抽象方法没有实现,子类继承必须实现,为子类提供规范的模板
        abstract public void dog(); //抽象方法
        
        public void run() {
          System.out.println("跑啊跑啊跑~");
        }
      }
      class Dog extends Test{
        @Override
        public void dog() {
          System.out.println("汪汪汪!"); //对父类抽象方法的实现
        }
      }

    接口

    • 接口的作用:
      • 接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束,全面专业的实现了:规范和具体实现的分离;抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法,接口是完全面向规范的,规定了一批类具有的公共方法规范。
      • 从接口的实现者角度看,接口定义了可以向外部提供的服务;从接口的调用者角度看,接口定义了实现者能提供哪些服务。
      • 接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。做系统时往往就是使用“面向接口”的思想来设计系统。
      • 接口和实现类不是父子关系,是实现规则的关系。比如:我定义一个接口Runnable,Car实现它就能在地上跑,Train实现它也能在地上跑,飞机实现它也能在地上跑。就是说,如果它是交通工具,就一定能跑,但是一定要实现Runnable接口。
    • 接口的本质:
      • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞;如果你是汽车,则必须能跑;如果你是警察,则必须维护治安。接口的本质是契约,就像我们现实中的法律一样,制定好后大家都遵守。面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
    • 接口与其他类区别:
      • 普通类可以具体实现;抽象类可以具体实现、规范(抽象方法);而接口只定义规范不定义具体实现。
    • 接口定义方法:
      // 接口定义格式
      [访问修饰符] interface <接口名> [extends 父接口1,父接口2,...]{
        常量定义;
        方法定义;
      }
      // 例子
      public interface Test { //接口,接口没有私有常量、方法
        int maxAge = 130; //默认为常量,自动使用 public static final 修饰
        void test(); //接口下所有方法都是抽象方法,自动使用 public abstract
      }
    • 接口使用方法:
      // 接口使用格式
      class <类名> implements <接口1,接口2,接口3,...>{
        方法的实现;
      }
      class Test2 implements Test{ //使用接口
        @Override
        public void test() { //实现方法
          System.out.println("测试接口");
        }
      }
    • 接口的多继承:接口支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。
      interface A {
        void testa();
      }
      interface B {
        void testb();
      }
      // 接口可以多继承:接口C继承接口A和B
      interface C extends A, B {
        void testc();
      }
      public class Test implements C {
        public void testc() {
        }
        public void testa() {
        }
        public void testb() {
        }
      }

    面向对象 - 内部类

    内部类(innerclasses)

    • 内部类的概念:一般情况,把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,称为内部类(innerclasses)。内部类可以使用public、default、protected 、private以及static修饰,而外部顶级类只能使用public和default修饰。
    • 内部类的分类:内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。
      • 成员内部类:可使用private、default、protected、public任意访问修饰符修饰,类文件名<外部类>$<内部类>.class
      • 非静态内部类:外部类里使用非静态内部类和平时使用其他类没什么不同
        • 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
        • 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
        • 非静态内部类不能有静态方法、静态属性和静态初始化块。
        • 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
        • 成员变量访问要点:内部类里方法的局部变量<变量名>;内部类属性<this.变量名>;外部类属性<外部类名.this.变量名>。
          package me.hzyis.test;
           
          public class Test {
            public static void main(String[] args) {
              // 创建内部类对象
              Outer.Inner inner = new Outer().new Inner();
              inner.show();
              
            }
          }
          class Outer{
            private int age = 10;
            public void testOuter() {
              System.out.println("Outer.testOuter()");
            }
            // 非静态内部类,添加修饰符 static 则为静态内部类
            class Inner{
              int age = 20;
              public void show() {
                int age = 30;
                System.out.println("外部类的成员变量age:" + Outer.this.age);
                System.out.println("内部类的成员变量age:" + this.age);
                System.out.println("内部类的局部变量age:" + age);
              }
            }
          }
      • 静态内部类:使用static修饰。
        • 当一个静态内部类对象存在,并不一定存在对应的外部类对象。因此,静态内部类的实例方法不能直接访问外部类的实例方法。
        • 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:“静态内部类.名字”的方式访问静态内部类的静态成员,通过 new 静态内部类()访问静态内部类的实例。
          package me.hzyis.test;
           
          public class Test {
            public static void main(String[] args) {
              // 创建静态内部类对象
              Outer.Inner inner = new Outer.Inner(); 
            }
          }
          class Outer{
            static class Inner{
            }
          }
      • 匿名内部类:适合那种只需要使用一次的类。比如:键盘监听操作等等。
        • 匿名内部类没有访问修饰符。
        • 匿名内部类没有构造方法,因为它连名字都没有那又何来构造方法呢。
          package me.hzyis.test;
           
          public class Test {
            public static void test(AA testa) { //传入接口对象
              System.out.println("##############");
              testa.aa();
            }
            public static void main(String[] args) {
              Test.test(new AA() { //匿名内部类
                @Override
                public void aa() {
                  System.out.println("测试匿名内部类");
                };
              });
            }
          }
          interface AA{
            void aa();
          }
      • 局部内部类:定义在方法内部的,作用域只限于本方法,称为局部内部类;局部内部类的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
      • package me.hzyis.test;
         
        public class Test {
          public static void main(String[] args) {
              new Test().show();
          }
          public void show() {
            // 局部内部类,作用域仅限于该方法
            class Inner{
              public void fun() {
                System.out.println("测试局部内部类");
              }
            }
            new Inner().fun();
          }
        }

    面向对象 - String类和常量池

    String类

    • String类基础:String类又被称作不可变字符序列,Java字符串就是Unicode字符序列;Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例;String位于java.lang包中,Java程序默认导入java.lang包下的所有类。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          String a = "测试"; //定义字符变量
          String b = new String("测试2"); //创建String类对象
          String c = "测试"+"3"; //字符串拼接
          String d = "测试"+4; //有一个字符串,+号则变为字符串连接符
          System.out.println(a+"\r\n"+b+"\r\n"+c+"\r\n"+d);
        }
      }
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          String s1 = "core Java";
          String s2 = "Core Java";
          System.out.println(s1.charAt(3)); //提取下标为3的字符,从0开始索引,所以第三个值为"e",不可为负数
          System.out.println(s2.length()); //字符串的长度,算上空格共10个字符
          
          System.out.println(s1.equals(s2)); //比较两个字符串是否相等
          System.out.println(s1.equalsIgnoreCase(s2)); //比较两个字符串是否相等(忽略大小写)
          
          System.out.println(s1.indexOf("Java")); //字符串s1中是否包含"Java"字符,如果存在返回指定字符在序列的位置
          System.out.println(s1.indexOf("java")); //字符串s1中是否包含"java"字符,如果没有返回-1
          
          String s = s1.replace(' ','&'); //将s1中的空格替换为&符号,替换后赋值给s,s1不会有任何变化
          System.out.println("result is:" + s);
          System.out.println("\r\n****************************\r\n");
          
          String st = "";
          String st1 = "How are you?";
          System.out.println(st1.startsWith("How")); //是否以How开头
          System.out.println(st1.endsWith("you")); //是否以You结尾
          
          st = st1.substring(4); //从下标为4的字符开始 到 字符串结尾为止,提取子字符串
          System.out.println(st);
          st = st1.substring(4,7); //提取下标是4到7,包括4不包括7的字符串
          System.out.println(st);
          
          st = st1.toLowerCase(); //全部转小写
          System.out.println(st);
          st = st.toUpperCase(); //全部转大写
          System.out.println(st);
          
          String st2 = " How old are you!! ";
          st = st2.trim(); //去除首尾空格,无法去除中间的空格
          System.out.println(st); //st2不变,生成新的st字符串输出
          System.out.println(st2); //因为String是不可变字符序列,所以st2不变
        }
      }

    常量池

    • 全局字符串常量池(String pool):全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。
    • class文件常量池(Class Constant Pool):class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用。
    • 运行时常量池(Runtime Constant Pool):运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          String test1 = "abc";
          String test2 = "abc";
          String test3 = new String("abc");
          System.out.println("是否相同:" + (test1==test2)); //test1和test2处在相同对象中,所有为true
          System.out.println("是否相同:" + (test3==test2)); //test3是不同对象,所以为false
        }
      }

    初级提升 - 常用类

    常用类

    常用类:待完善


    初级提升 - 数组

    数组(Array)

    • 数组的简介:数组是相同类型数据的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。数组的基本特点:
      • 长度是确定的,数组一旦被创建,它的大小就是不可以改变的
      • 其元素必须是相同类型,不允许出现混合类型
      • 数组类型可以是任何数据类型,包括基本类型和引用类型
      • 数组变量属引用类型,数组本就是对象,数组中的每个元素相当于该对象的成员变量
      • Java中对象存在于堆中,因此数组无论保存原始类型还是其他对象类型,数组对象本身存储在堆中。
        // 数组声明格式
        type[] 数组名; //建议这种
        type 数组名[];
         
        package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
            int[] arr01 = new int[10]; //[10]表示数组长度为10,可放置10个元素
            
            String[] arr02 = null; //声明数组,但不赋予任何值,此时栈内存中有arr02变量,但堆内存中什么也没有
            arr02 = new String[5]; //给arr02数组赋值,此时堆内存中创建数组对象String[],在对象中数组各个元素遵守成员变量的规则,比如int默认是0、布尔默认是false
            // 给数组各个元素赋值,索引下标从0开始,范围0到(length-1),也就是0-4
            {
              arr02[0] = "1号元素";
              arr02[1] = "2号元素";
              arr02[2] = "3号元素";
              arr02[3] = "4号元素";
              arr02[4] = "5号元素";
            }
            System.out.println(arr02[0]+"\r\n"+arr02[1]+"\r\n"+arr02[2]+"\r\n"+arr02[3]+"\r\n"+arr02[4]);
            
            double arr03[] = new double[7];
            // 利用循环赋值并输出
            for(int i=0; i<arr03.length; i++) {
              arr03[i] = i+1;
              System.out.println(arr03[i]);
            }
            
            User[] arr04 = new User[4]; //给引用类型数组分配空间
            // 输入内容
            arr04[0] = new User(0001,"黄泽风");
            arr04[1] = new User(0002,"黄泽雨");
            arr04[2] = new User(0003,"黄泽雷");
            arr04[3] = new User(0004,"黄泽电");
            for(int i=0; i<arr04.length; i++) {
              System.out.println(arr04[i].getName()); //输出数组中的name属性
            }
          }
        }
        class User{
          private int id;
          private String name;
          
          public User(int id,String name){
            this.id = id;
            this.name = name;
          }
          public int getId() {
            return id;
          }
          public void setId(int id) {
            this.id = id;
          }
          public String getName() {
            return name;
          }
          public void setName(String name) {
            this.name = name;
          }
        
        }
        
    • 数组的初始化:数组的初始化方式共三种:静态初始化、动态初始化、默认初始化。
      • 默认初始化:
        package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
            //默认初始化,默认给数组的元素进行赋值,默认的赋值规则和成员变量相同
            int[] i = new int[2]; //int默认为0
            boolean[] b = new boolean[2]; //boolean默认为false
            String[] s = new String[2]; //String默认为null
          }
        }
      • 静态初始化:
        package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
            int[] a = {2,5,10}; //静态初始化,基本类型数组,直接赋值,赋几个值长度就是几
            // 静态初始化,引用类型数组
            User[] b = {
                new User(0001,"黄泽风"),
                new User(0002,"黄泽雨"),
                new User(0003,"黄泽雷"),
                new User(0004,"黄泽电")
            };
          }
        }
        class User{
          private int id;
          private String name;
          public User(int id, String name) {
            this.id = id;
            this.name = name;
          }
          public int getId() {
            return id;
          }
          public void setId(int id) {
            this.id = id;
          }
          public String getName() {
            return name;
          }
          public void setName(String name) {
            this.name = name;
          }
        }
      • 动态初始化:
        package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
            int[] i = new int[2]; //动态初始化数组,分配空间
            //给数组元素赋值
            i[0] = 1;
            i[1] = 2;
            System.out.println(i[0] +"\r\n"+ i[1]);
          }
        }
    • 数组的遍历:数组元素下标的合法区间:[0, length-1]。我们可以通过下标来遍历数组中的元素,遍历时可以读取元素的值或者修改元素的值。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          int[] a = new int[4];
          // 初始化数组的值
          for(int i=0; i<a.length; i++) {
            a[i] = 100+1;
          }
          // 读取元素的值
          for(int i=0; i<a.length; i++) {
            System.out.println(a[i]);
          }
        }
      }
    • for-each循环:增强for循环for-each是JDK1.5新增加的功能,专门用于读取数组或集合中所有的元素,即对数组进行遍历。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          int[] a = new int[4];
          for(int i=0; i<a.length; i++) {
            a[i] = 100+1;
          }
      	// for-each循环,快速遍历数组
          for(int m:a) {
            System.out.println(m);
          }
        }
      }
    • 数组的拷贝和应用:使用System类中的static void arraycopy(object src,int srcpos,object dest, int destpos,int length)方法,对数组进行拷贝。删除、插入某个数据都可以利用拷贝来完成。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          ArrayCopy ac = new ArrayCopy();
          ac.testArray();
          ac.removeElment();
          ac.testRemoveElmentTool();
          ac.extendRange();
          ac.insertArray();
        }
      }
      class ArrayCopy{
        String[] str = {"黄泽风","黄泽雨","黄泽雷","黄泽电"};
        String[] str2 = {"黄泽风2","黄泽雨2","黄泽雷2","黄泽电2"};
        String[] str3 = {"黄泽风3","黄泽雨3","黄泽雷3","黄泽电3"};
        String[] str4 = {"黄泽风4","黄泽雨4","黄泽雷4","黄泽电4"};
        // 测试数组的拷贝
        public void testArray(){
          String[] strBak = new String[6];
          // 拷贝格式:System.arraycopy(src, srcPos, dest, destPos, length);
          // System.arraycopy(<原数组>, <原数组开始位置>, <目标数组>, <目标数组开始位置>, <拷贝长度>);
          System.arraycopy(str, 0, strBak, 1, 4);
          for(int i=0; i<strBak.length; i++) {
            System.out.println(i+". "+strBak[i]);
          }
        }
        
        // 利用拷贝删除数组某个位置某个元素
        public void removeElment(){
          System.out.println("*************************************");
          // 删除数组中间的数,本质上还是拷贝,例如删掉下标1的内容”黄泽雨“
          System.arraycopy(str, 2, str, 1, 2); //将2、3拷贝覆盖1、2
          str[str.length-1] = null; //将多出的3改为null
          for(int i=0; i<str.length; i++) {
            System.out.println(i+". "+str[i]);
          }
        }
        
        // 封装一个利用拷贝删除数组的工具
        public String[] removeElmentTool(String[] array, int index){
          System.arraycopy(array, index+1, array, index, array.length-index-1);
          array[array.length-1] = null;
          for(int i=0; i<array.length; i++) {
            System.out.println(i+". "+array[i]);
          }
          return array;
        }
        public void testRemoveElmentTool() {
          System.out.println("*************************************");
          removeElmentTool(str2, 1);
        }
        
        // 扩容数组,本质上是创建一个更大的数组,将原数组数据拷贝到更大的数组中
        public void extendRange() {
          System.out.println("*************************************");
          String[] arrayExtend = new String[str3.length+3]; //创建一个比str3数组大3的数组
          System.arraycopy(str3, 0, arrayExtend, 0, str3.length); //将str3数组数据拷贝到arrayExtend数组中去
          for(int i=0; i<arrayExtend.length; i++) {
            System.out.println(i+". "+arrayExtend[i]);
          }
        }
        
        // 数组插入数据
        public void insertArray(){
          System.out.println("*************************************");
          String[] arrayExtend2 = new String[str3.length+3];
          System.arraycopy(str3, 0, arrayExtend2, 0, str3.length); //将str3数据拷贝到更大的数组arrayExtend2内
          System.arraycopy(str4, 0, arrayExtend2, 1, 1); //将str4中的下标为0的数据插入到arrayExtend2的下标1中
          System.arraycopy(str3, 1, arrayExtend2, 2, str3.length-1); //将被覆盖的数据重新拷贝回来,完成插入数据
          for(int i=0; i<arrayExtend2.length; i++) {
            System.out.println(i+". "+arrayExtend2[i]);
          }
        }
      }
    • java.util.Arrays类:JDK提供的java.util.Arrays类,包含了常用的数组操作,方便日常开发。Arrays类包含了:排序、查找、填充、打印内容等常见的操作。
      package me.hzyis.test;
      import java.util.Arrays;
      public class Test {
        public static void main(String[] args) {
          int[] a = {1,2,3};
          System.out.println(a); //输出string和a的哈希值
          System.out.println(Arrays.toString(a)); //输出数组内容
          
          System.out.println("*****************************");
          int[] b = {100,50,30,200,10};
          System.out.println(Arrays.toString(b));
          Arrays.sort(b); //从小到大排序
          System.out.println(Arrays.toString(b));
          
          int[] c = {100,50,30,200,10};
          System.out.println("30的索引位置:"+Arrays.binarySearch(c, 30)); //查找30所在的索引位置
          System.out.println("-30的索引位置:"+Arrays.binarySearch(c, -30)); //如果查找的值不存在则返回负值
        }
      }
    • 多维数组:多维数组可以看成以数组为元素的数组(数组套数组),可以有二维、三维、甚至更多维数组(套多层数组)。
      • 二维数组:
        package me.hzyis.test;
        public class Test {
          public static void main(String[] args) {
            int[] arr01 = new int[3]; //一维int类型数组
            Car[] cars = new Car[3]; //一维引用类型数组
            
            int[][] arr02 = new int[3][]; //二维int类型数组,数组套数组
            arr02[0] = new int[] {10,20}; //
            arr02[1] = new int[] {30,40};
            arr02[2] = new int[] {50,60,70};
            System.out.println(arr02[2][2]);
            
            // 静态初始化二维数组
            int[][] arr03 = {
                {10,20},
                {100,200},
                {1000,2000}
            };
            System.out.println(arr03[2][1]);
          }
        }
        class Car{}
      • 自学JAVA知识点总结 - 初级

    • 数组存储表格数据:表格数据模型是计算机世界最普遍的模型,可以说互联网上看到的所有数据本质上都是“表格”,无非是表格之间互相套用。
      package me.hzyis.test;
      import java.util.Arrays;
      public class Test {
        public static void main(String[] args) {
          // 此处001本质不是Object对象,编译器会把基本数据类型“自动装箱”成包装类对象
          Object[] emp1 = {001,"黄泽风",20,"学员"};
          Object[] emp2 = {002,"黄泽雨",21,"学员"};
          Object[] emp3 = {003,"黄泽雷",22,"学员"};
          Object[] emp4 = {004,"黄泽电",23,"学员"};
          
          Object[][] tableData = new Object[4][];
          tableData[0] = emp1;
          tableData[1] = emp2;
          tableData[2] = emp3;
          tableData[3] = emp4;
          for(Object[] temp:tableData) {
            System.out.println(Arrays.toString(temp));
          }
        }
      }

    初级提升 - 容器

    容器

    • 容器(interface):开发和学习中需要时刻和数据打交道,一般通过“容器”容纳或管理容器,数组就是容器的一种。
      • 数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高,如果从效率和类型检查的角度来讲,数组是最好的;数组的劣势:不灵活,容量需要事先定义好,不能随着需求的变化而扩容,比如:用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?在写程序时是无法确定的,因此这种情况下就不能使用数组。
      • 基于数组并不能满足对于“管理和组织数据的需求”,所以需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是容器,也叫集合(Collection)。以下是容器的接口层次结构图:自学JAVA知识点总结 - 初级
    • 泛型(Generics):泛型是JDK1.5以后增加的,可以建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性;泛型的本质就是“数据类型的参数化”,可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          // 这里的”String”就是实际传入的数据类型;
          MyCollection mc = new MyCollection(); //
          mc.set("黄泽雨", 001);
          String a = mc.get(1); //加了泛型,直接返回String类型,不用强制转换;
          System.out.println(a);
        }
      }
      // 可以在类的声明处增加泛型列表,如:<T,E,V>。
      class MyCollection{
        Object[] objs = new Object[5];
        public void set(E e ,int index) { //E表示泛型
          objs[index] = e;
        }
        public E get(int index) {
          return (E)objs[index];
        }
      }
    • Collection接口:Collection表示一组对象,它是集中、收集的意思;Collection接口的两个子接口是List、Set接口;由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有下面的方法。
      方法说明
      boolean add(Object element)增加元素到容器中
      boolean remove(Object element)从容器中移除元素
      boolean contains(Object element)检查容器中是否包含该元素
      int size()容器中元素的数量
      boolean isEmpty()容器是否为空
      void clear()清空容器中的所有元素
      Iterator iterator()获得迭代器,用于遍历所有元素
      boolean containsAll(Collection c)本容器是否包含c容器中的所有元素
      boolean addAll(Collection c)将容器c中所有元素增加到本容器
      boolean removeAll(Collection c)移除本容器和容器c中相同的元素
      boolean retainAll(Collection c)取本容器和容器c中相同的元素,移除非交集元素
      Object[] toArray()转换成Object数组
      // list包含的方法,set相同
      package me.hzyis.test;
      import java.util.*;
      public class Test {
        public static void main(String[] args) {
          Collection c = new ArrayList<>();
          System.out.println("默认值:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          c.add("黄泽风");
          c.add("黄泽雨");
          System.out.println("添加后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
      
          System.out.println("************************");
          System.out.println("是否包含<黄泽风>:"+c.contains("黄泽风"));
          System.out.println("是否包含<黄泽雷>:"+c.contains("黄泽雷"));
          
          System.out.println("************************");
          c.remove("黄泽风"); //移除c对象中“黄泽风”的引用地址,不是删除元素本身,移除后元素仍在内存中
          System.out.println("移除后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          c.clear();
          System.out.println("清空后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          Object[] toObject = c.toArray(); //转换为Object数组
          System.out.println("转换后:"+toObject);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          Collection d = new ArrayList<>();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          System.out.println("d默认值:"+d);
          
          System.out.println("************************");
          d.addAll(c);
          System.out.println("d添加c后:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          d.removeAll(c);
          System.out.println("d移除和c的相同元素后:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          d.retainAll(c);
          System.out.println("d和c中相同元素:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          System.out.println("d是否包含c中所有元素:"+d.containsAll(c));
          c.remove("黄泽风");
          System.out.println("d是否包含c中所有元素:"+d.containsAll(c));
        }
      }
    • List接口:有序:List中每个元素都有索引标记,可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素;可重复:List允许加入重复的元素,更确切地讲,List通常允许满足e1.equals(e2) 的元素重复加入容器;除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法:
      方法说明
      void add(int index, Object element)在指定位置插入元素,前面的元素全部后移一位
      Object set(int index, Object element)修改指定位置的元素
      Object get(int index)返回指定位置的元素
      Object remove(int index)删除指定位置的元素,后面的元素全部前移一位
      nt indexOf(Object o)从前往后检索,根据提供的元素返回首个匹配元素的索引,如果没有该元素,返回-1
      int lastIndexOf(Object o)从后往前检索,根据提供的元素返回首个匹配元素的索引,如果没有该元素,返回-1
      package me.hzyis.test;
      import java.util.*;
      public class Test {
        public static void main(String[] args) {
          List list = new ArrayList<>();
          list.add("test01");
          list.add("test02");
          list.add("test03");
          list.add("test04");
          list.add("test05");
          System.out.println(list);
          
          System.out.println("*****************************");
          list.add(2,"黄泽雨");
          System.out.println("索引2插入元素后:"+list);
          
          System.out.println("*****************************");
          list.remove(3);
          System.out.println("删除索引3元素后:"+list);
          
          System.out.println("*****************************");
          list.set(0, "黄泽风");
          System.out.println("设置索引0元素后:"+list);
          
          System.out.println("*****************************");
          System.out.println("索引1元素是:"+list.get(1));
          
          System.out.println("*****************************");
          list.add(3,"test05");
          System.out.println("当前排序:"+list);
          System.out.println("查找test05的顺序索引位置:"+list.indexOf("test05"));
          
          System.out.println("查找test05的倒序索引位置:"+list.lastIndexOf("test05"));
        }
      }
    • ArrayList:ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率低,线程不安全。我们一般使用它。数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,其方法就是创建一个更长的数组后,将原数组数据拷贝进新数组,新数据存放在老数组后面,从而实现扩容。
      // get方法
      public E get(int index) {
        rangeCheck(index); //检查索引是否正确
        return elementData(index); // 返回对应内容
      }
       
      // add方法
      public boolean add(E e) {
        ensureCapacityInternal(size + 1); //检查数组空位是否足够,不够则+1
        elementData[size++] = e;
        return true;
      }
       
      // 构造器,如果传入数组长度>0,
      public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
          // 大于0则new一个指定长度的数组
          this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
          // 如果为0则传入EMPTY_ELEMENTDATA的默认长度10
          // private static final int DEFAULT_CAPACITY = 10;
          // private static final Object[] EMPTY_ELEMENTDATA = {};
          this.elementData = EMPTY_ELEMENTDATA;
        } else {
          throw new IllegalArgumentException("Illegal Capacity: "+
            initialCapacity);
        }
      }
       
      // 扩容
      private void grow(int minCapacity) {
        int oldCapacity = elementData.length; //老容量=数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量=老容量+(老容量/2),假设老容量=10,则新容量=15
        if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
          newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity); //把老数组元素拷贝到新数组后覆盖老数组,实现扩容
      }
    • LinkedList:LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

    初级提升 - IO技术

    IO技术

    IO技术:待完善


    初级提升 - 多线程技术

    多线程技术

    多线程技术:待完善


    初级提升 - 网络编程

    网络编程

    网络编程:待完善


    初级提升 - 常用算法

    常用算法

    常用算法:待完善


    初级提升 - 异常机制

    异常机制

    异常机制:待完善


© 版权声明

相关文章