2025年java零基础自学书

java零基础自学书前言 在网上一搜索 Java 学习会弹出无数文章 但全都是培训机构的大纲文 实在鸡肋 根本没写一个字是在帮助菜鸟怎么认识 Java 的 所以我琢磨着写一篇真正意义上的 Java 从 0 到 1 的文章 帮 0 基础的小白和初学的财年打开 Java 的大门 如果观察过招聘网站上的 Java 相关岗位需求就会发现 抛开其它的经验能力等等 单纯从技术 或者说知识上来讲 可以发现一些共通的地方 Java 基础 计算机基础 数据库

大家好,我是讯享网,很高兴认识大家。



前言

在网上一搜索Java学习会弹出无数文章,但全都是培训机构的大纲文,实在鸡肋,根本没写一个字是在帮助菜鸟怎么认识Java的,所以我琢磨着写一篇真正意义上的Java从0到1的文章,帮0基础的小白和初学的财年打开Java的大门

如果观察过招聘网站上的Java相关岗位需求就会发现,抛开其它的经验能力等等,单纯从技术,或者说知识上来讲,可以发现一些共通的地方。

  • Java基础
  • 计算机基础
  • 数据库,SQL/NoSQL
  • 常用开源框架
  • 分布式/微服务
  • 中间件,缓存、消息中间件

学习任何一门编程语言,首先要学习的是基础语法,开启Java学习的第一步,当然就是深入掌握计算机基础、编程基础语法,面向对象,集合、IO流、线程、并发、异常及网络编程,这些我们称之为JavaSE基础。当你掌握了这些内容之后,你就可以做出诸如:电脑上安装的迅雷下载软件、聊天客户端、考勤管理系统等桌面端软件。

而这篇文章要写的,就是Java基础,有人看的话后面可能也会继续写计算机基础、数据库和框架这些。

在文末我也整理了一份书单Java架构师学习核心书单,各位可以按照自己的学习进度去酌情购买,如果是学生或者最近手头紧,也没有关系,我给大家收集了电子档,需要的朋友可以看我首页。

  • 22本Java架构师核心书籍
  • 从0到1Java学习路线和资料
  • 1000+道2021年最新面试题

话不多说,坐稳扶好,发车喽!

一、Java基础语法

关键字的概念与特征

概念:Java关键字是事先定义好的对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。

特征:

  • 完全小写
  • 在增强版记事本中(例如Notepad++)有特殊颜色

2、标识符的概念与规则

什么是标识符?

凡是可以由自己命名的地方都称为标识符.

例: 项目名 ,包名 ,类名 .方法名

命名规范

①   不可使用java关键字和保留字,但是可以包含关键字和保留字.

②    可以使用26个字母大小写,数字0-9,$和_.

③    可以使用数字,但不可放在首位.

④    长度理论上没有限制,但命名最好能反映出其作用,遵循”驼峰形式”,见命知意

⑤    包名全部小写,类名全部大驼峰式

⑥   变量名、方法名首字母小写,如果名称由多个单词组成,每个单词的首字母都要大写

⑦ 常量(特指被 final 修饰的常量)名全部大写,如果是单个字母,需要大写,如果是多个单词,需要用下划线分开。并且都大写

例:写一个ajax的项目实例,

常量&变量

常量的概念:在程序运行期间。固定不变的量。

常量的分类:

  • 字符常量:凡是用单引号引起来的单个字符,就做字符常量。例如:、‘b'、9’、‘中
  • 字符串常量:凡是用双引号引起来的部分,叫做字符串常量。例如:"abc"、"Hello”、“123"
  • 整数常量:直接写上的数字,没有小数点。例如:100、200、0、-250
  • 浮点数常量:直接写上的数字,有小数点。例如:2.5、-3.14、0.0 
  • 布尔常量:只有两种取值 true  |  false

基本数据类型:

注意事项:

  • Java中的默认类型:整数类型是 int 、浮点类型是double
  • 数据范围与字节数不一定相关,例如float数据范围比 long 更加广泛,但是float是4字节,1ong是8字节。
  • 浮点型可能只是一个近似值,并非精确的值。
  • 浮点数当中默认类型是double。如果一定要使用float类型,需要加上一个后缀F。
  • 字符串不是基本类型,而是引用类型
  • 如果是整数,默认为int类型,如果一定要使用long类型,需要加上一个后缀L。推荐使用大写字母后缀。

变量: 程序运行期间,内容可以改变的量。

创建一个变量并且使用的格式:

  数据类型  变量名称; //创建了一个变量

  变量名称 = 数据值;  //赋值,将右边的数据值,赋值交给左边的变量

一步到位的格式:  数据类型  变量名称 = 数据值; //在创建一个变量的同时,立刻放入指定的数据值

注意事项:

  1.如果创建多个变量,那么变量之间的名称不可以重复。

   2.对于float和long类型来说,字母后缀F 和 L不要丢掉。

   3.没有进行赋值的变量,不能直接使用;一定要赋值之后,才能使用。

作用域 :从定义变量的一行开始,一直到直接所属的大括号结束。 

数据类型转换

当效据类型不一样时,就会发生数据类型转换

自动类型转换(隐式)

  1.特点:代码不需要进行特殊处理,自动完成

  2.规则:数据范围从小到大

 // 左边是long类型,右边是默认的int类型,左右不一样

 // int-->long,符合了数据范围从小到大的要求

强制类型转换(显式)

   1.特点:代码需要进行特殊的格式处理,不能自动完成

   2.格式:范围小的类型范围小的变量名=(范围小的类型)原本范围大的数据

 //左边是int类型,右边是long类型,不一样

 //格式范围小的类型  范围小的变量名 =(范围小的类型) 原本范围大的数据 ;

注意事项:

1.强制类型转换一般不推荐使用,因为有可能发生精度损失、数据溢出。

  2.byte / short / char这三种类型都可以发生数学运算,例如加法“+”.

  3.byte/short/char这三种类型在运算的时候,都会被首先向上提升成为int类型,然后再计算。符合ASCII编码表。

java零基础自学书 运算符 :进行特定操作的符号。例如:+

表达式:用运算符连起来的式子叫做表达式。例如:20+5 。   又例如:a+b

 四则运算:加+      减 -      乘 *      除  /

取模(取余数):%

只有对于整数的除法来说,取模运算符才有余数的意义。

算术运算符

 

 四则运算当中的加号“+”有常见的三种用法:

  1.对于数值来说,+ 就是加法。

  2.对于字符char类型来说,在计算之前,char会被提升成为int,然后再计算。   char类型字符,和int类型数字,之间的对照关系表:ASCII、Unicode

  3.对于字符串String来说,+ 代表字符串连接操作。() 小括号的优先级最高

自增运算符:++

  例如:   a++;   先使用a值,再执行a = a + 1 ;

自减运算符:- -

例如:   ++a; 先执行a = a + 1 ; 再使用a值。

a++ : 在变量值被使用之后才增加的值

++a : 在变量值在被使用之前增加它的值


    • 操作符的工作原理与此相同,只是它所执行的是减值操作而非增值操作。

赋值运算符

赋值运算符是指为变量或常量指定数值的符号。如可以使用 “=” 将右边的表达式结果赋给左边的操作数。

Java 支持的常用赋值运算符,如下表所示:

比较运算符

比较运算符用于判断两个数据的大小,例如:大于、等于、不等于。比较的结果是一个布尔值( true 或 false )。

Java 中常用的比较运算符如下表所示:

注意事项:

  1、  > 、 < 、 >= 、 <= 只支持左右两边操作数是数值类型

  2、  == 、 != 两边的操作数既可以是数值类型,也可以是引用类型

逻辑运算符

主要用于进行逻辑运算,连接两个Boolean值,代表两个条件。

Java 中常用的逻辑运算符如下表所示:

我们可以从“投票选举”的角度理解逻辑运算符:

  1、 与:要求所有人都投票同意,才能通过某议题

  2、 或:只要求一个人投票同意就可以通过某议题

  3、 非:某人原本投票同意,通过非运算符,可以使其投票无效

  4、 异或:有且只能有一个人投票同意,才可以通过某议题

当使用逻辑运算符时,我们会遇到一种很有趣的 “短路” 现象  >> 如果根据左边已经可以判断得到最终结果,那么右边的代码将不再执行,从而节省一定的性能。

譬如:( 1 > 2 ) && ( 1 < 3 ) 中,如果能确定左边 1 > 2 运行结果为 false , 则系统就认为已经没有必要执行右侧的 1 < 3 啦。

条件运算符

条件运算符( ? : )也称为 “三元运算符”。

  • 一元运算符:只需要一个数据就可以进行操作的运算符。例如:取反!、自增++、自减--
  • 二元运算符:需要两个数据才可以进行操作的运算符。例如:加法+、赋值=
  • 三元运算符:需要三个数据才可以进行操作的运算符。

语法形式:布尔表达式 ? 表达式1 :表达式2

运算过程:如果布尔表达式的值为 true ,则返回 表达式1 的值,否则返回 表达式2 的值

例如 :

 因为,表达式 8>5 的值为 true ,所以,返回: 8大于5

方法 ♦ ♦

所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块。

一般情况下,定义一个方法的语法是:  

 其中:

  1、 访问修饰符:方法允许被访问的权限范围, 可以是 public、protected、private 甚至可以省略 ,其中 public 表示该方法可以被其他任何代码调用

  2、 返回值类型:方法返回值的类型,如果方法不返回任何值,则返回值类型指定为 void ; 如果方法具有返回值,则需要指定返回值的类型,并且在方法体中使用 return 语句返回值

  3、 方法名:定义的方法的名字,必须使用合法的标识符

  4、 参数列表:传递给方法的参数列表,参数可以有多个,多个参数间以逗号隔开,每个参数由参数类型和参数名组成,以空格隔开

根据方法是否带参、是否带返回值,可将方法分为四类:

   无参无返回值方法

  如果方法不包含参数,且没有返回值,我们称为无参无返回值的方法。

  方法的使用分两步:

  第一步,定义方法

  例如:下面代码定义了一个方法名为 show ,没有参数,且没有返回值的方法,执行的操作为输出 “ welcome to imooc. ”

  

注意 :

  1、 方法体放在一对大括号中,实现特定的操作

  2、 方法名主要在调用这个方法时使用,需要注意命名的规范,一般采用第一个单词首字母小写,其它单词首字母大写的形式

 第二步,调用方法

  当需要调用方法执行某个操作时,可以先创建类的对象,然后通过  对象名.方法名();  来实现

  例如:在下面的代码中,我们创建了一个名为 hello 的对象,然后通过调用该对象的 show( ) 方法输出信息

  

  运行结果为:  welcome to imooc.

无参带返回值方法

  如果方法不包含参数,但有返回值,我们称为无参带返回值的方法。

  例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值为 int 类型的方法,执行的操作为计算两数之和,并返回结果

  

    在 calSum( ) 方法中,返回值类型为 int 类型,因此在方法体中必须使用 return 返回一个整数值

  调用带返回值的方法时需要注意,由于方法执行后会返回一个结果,因此在调用带返回值方法时一般都会接收其返回值并进行处理。如下:第6行

  

  运行结果为: 两数之和为:17

不容忽视的“小陷阱”:

  1、 如果方法的返回类型为 void ,则方法中不能使用 return 返回值!

  

  2、 方法的返回值最多只能有一个,不能返回多个值

  

  3、 方法返回值的类型必须兼容,例如,如果返回值类型为 int ,则不能返回 String 型值

  

带参无返回值方法

 有时方法的执行需要依赖于某些条件,换句话说,要想通过方法完成特定的功能,需要为其提供额外的信息才行。

 例如,现实生活中电饭锅可以实现“煮饭”的功能,但前提是我们必须提供食材,如果我们什么都不提供,那就真是的“巧妇难为无米之炊”了。

我们可以通过在方法中加入参数列表接收外部传入的数据信息,参数可以是任意的基本类型数据或引用类型数据。

 我们先来看一个带参数,但没有返回值的方法:

 

  上面的代码定义了一个 show 方法,带有一个参数 name ,实现输出欢迎消息。

  调用带参方法与调用无参方法的语法类似,但在调用时必须传入实际的参数值

  

例如:

  运行结果为: 欢迎您,北游!

很多时候,我们把定义方法时的参数称为形参,目的是用来定义方法 需要传入参数的个数和类型;把调用方法时的参数称为实参,是传递给方法真正被处理的值。

 一定不可忽视的问题:

  1、 调用带参方法时,必须保证实参的数量、类型、顺序与形参一一对应

  

2、 调用方法时,实参不需要指定数据类型,如 []

3、 方法的参数可以是基本数据类型,如 int、double 等,也可以是引用数据类型,如 String、数组等         

 第10行,Array.toString()方法将数组转换为字符串输出

4、 当方法参数有多个时,多个参数间以逗号分隔

 带参带返回值方法

  如果方法既包含参数,又带有返回值,我们称为带参带返回值的方法。

  例如:

下面的代码,定义了一个 show 方法,带有一个参数 name ,方法执行后返回一个 String 类型的结果

  

调用带参带返回值的方法:

运行结果为: 欢迎您,北游!

二、Java面向对象

面向对象

  面向对象(Object Oriented)是一种新兴的程序设计方法,或者是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。

对象

  对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。

  类的实例化可生成对象,一个对象的生命周期包括三个阶段:生成、使用、消除。

  当不存在对一个对象的引用时,该对象成为一个无用对象。Java的垃圾收集器自动扫描对象的动态内存区,把没有引用的对象作为垃圾收集起来并释放。当系统内存用尽或调用System.gc( )要求垃圾回收时,垃圾回收线程与系统同步运行。

  类是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和方法两个主要部分。在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属性和方法两个主要部分。

  Java中的类实现包括两个部分:类声明和类体。

  类声明

  其中,修饰符public,abstract,final 说明了类的属性,className为类名,superclassName为类的父类的名字,interfaceNameList为类所实现的接口列表。

  类体

  成员变量限定词的含义:

  • static: 静态变量(类变量)
  • final: 常量;transient: 暂时性变量,用于对象存档,用于对象的串行化
  • volatile: 贡献变量,用于并发线程的共享

  方法的实现也包括两部分内容:方法声明和方法体。

  方法声明

  方法声明中的限定词的含义:

  • static: 类方法,可通过类名直接调用
  • abstract: 抽象方法,没有方法体
  • final: 方法不能被重写
  • native: 集成其它语言的代码
  • synchronized: 控制多个并发线程的访问

  方法声明包括方法名、返回类型和外部参数。其中参数的类型可以是简单数据类型,也可以是复合数据类型(又称引用数据类型)。   对于简单数据类型来说,java实现的是值传递,方法接收参数的值,但不能改变这些参数的值。如果要改变参数的值,则用引用数据类型,因为引用数据类型传递给方法的是数据在内存中的地址,方法中对数据的操作可以改变数据的值。

  方法体

  方法体是对方法的实现,它包括局部变量的声明以及所有合法的Java指令。方法体中声明的局部变量的作用域在该方法内部。若局部变量与类的成员变量同名,则类的成员变量被隐藏。   为了区别参数和类的成员变量,我们必须使用this。this用在一个方法中引用当前对象,它的值是调用该方法的对象。返回值须与返回类型一致,或者完全相同,或是其子类。当返回类型是接口时,返回值必须实现该接口。

  构造方法

  • 构造方法是一个特殊的方法。Java 中的每个类都有构造方法,用来初始化该类的一个对象。
  • 构造方法具有和类名相同的名称,而且不返回任何数据类型。
  • 重载经常用于构造方法。
  • 构造方法只能由new运算符调用

面向对象的基本特性

  封装

  封装性就是尽可能的隐藏对象内部细节,对外形成一道边界,只保留有限的接口和方法与外界进行交互。封装的原则是使对象以外的部分不能随意的访问和操作对象的内部属性,从而避免了外界对对象内部属性的破坏。

  可以通过对类的成员设置一定的访问权限,实现类中成员的信息隐藏。

  • private:类中限定为private的成员,只能被这个类本身访问。如果一个类的构造方法声明为private,则其它类不能生成该类的一个实例。
  • default:类中不加任何访问权限限定的成员属于缺省的(default)访问状态,可以被这个类本身和同一个包中的类所访问。
  • protected:类中限定为protected的成员,可以被这个类本身、它的子类(包括同一个包中以及不同包中的子类)和同一个包中的所有其他的类访问。
  • public:类中限定为public的成员,可以被所有的类访问。

  继承

  子类的对象拥有父类的全部属性与方法,称作子类对父类的继承。

  • Java中父类可以拥有多个子类,但是子类只能继承一个父类,称为单继承。
  • 继承实现了代码的复用。
  • Java中所有的类都是通过直接或间接地继承java.lang.Object类得到的。
  • 子类不能继承父类中访问权限为private的成员变量和方法。
  • 子类可以重写父类的方法,即命名与父类同名的成员变量。

  Java中通过super来实现对父类成员的访问,super用来引用当前对象的父类。super 的使用有三种情况:

  • 访问父类被隐藏的成员变量,如:super.variable;
  • 调用父类中被重写的方法,如:super.Method([paramlist]),super()调用父类构造方法;
  • 调用父类的构造函数,如:super([paramlist]);

  多态

  对象的多态性是指在父类中定义的属性或方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或方法在父类及其各个子类中具有不同的语义。例如:"几何图形"的"绘图"方法,"椭圆"和"多边形"都是"几何图"的子类,其"绘图"方法功能不同。

  Java的多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)。

  • 编译时多态:在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。
  • 运行时多态:由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。

  重载(Overloading)

  • 方法重载是让类以统一的方式处理不同数据类型的手段。
  • 一个类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法。
  • 返回值类型可以相同也可以不相同,无法以返回型别作为重载函数的区分标准。

  重写(Overriding)

  • 子类对父类的方法进行重新编写。如果在子类中的方法与其父类有相同的的方法名、返回类型和参数表,我们说该方法被重写 (Overriding)。
  • 如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
  • 子类函数的访问修饰权限不能低于父类的。

三、Java集合

集合的由来

  通常,我们的程序需要根据程序运行时才知道创建多少个对象。但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型。为了满足这些常规的编程需要,我们要求能在任何时候,任何地点创建任意数量的对象,而这些对象用什么来容纳呢?我们首先想到了数组,但是数组只能放统一类型的数据,而且其长度是固定的,那怎么办呢?集合便应运而生了!

集合是什么?

Java集合类存放于 java.util 包中,是一个用来存放对象的容器。

注意: ①、集合只能存放对象。比如你存一个 int 型数据 1放入集合中,其实它是自动转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。

②、集合存放的是多个对象的引用,对象本身还是放在堆内存中。

③、集合可以存放不同类型,不限数量的数据类型。

Java 集合框架图

此图来源于:blog.csdn.net/u0/…

大图访问

发现一个特点,上述所有的集合类,除了 map 系列的集合,即左边集合都实现了 Iterator 接口,这是一个用于遍历集合中元素的接口,主要hashNext(),next(),remove()三种方法。它的一个子接口 ListIterator 在它的基础上又添加了三种方法,分别是 add(),previous(),hasPrevious()。也就是说如果实现 Iterator 接口,那么在遍历集合中元素的时候,只能往后遍历,被遍历后的元素不会再被遍历到,通常无序集合实现的都是这个接口,比如HashSet;而那些元素有序的集合,实现的一般都是 LinkedIterator接口,实现这个接口的集合可以双向遍历,既可以通过next()访问下一个元素,又可以通过previous()访问前一个 元素,比如ArrayList。

还有一个特点就是抽象类的使用。如果要自己实现一个集合类,去实现那些抽象的接口会非常麻烦,工作量很大。这个时候就可以使用抽象类,这些抽象类中给我们提供了许多

现成的实现,我们只需要根据自己的需求重写一些方法或者添加一些方法就可以实现自己需要的集合类,工作量大大降低。

集合详解

①、Iterator:迭代器,它是Java集合的顶层接口(不包括 map 系列的集合,Map接口 是 map 系列集合的顶层接口)

  Object next():返回迭代器刚越过的元素的引用,返回值是 Object,需要强制转换成自己需要的类型

  boolean hasNext():判断容器内是否还有可供访问的元素

  void remove():删除迭代器刚越过的元素

所以除了 map 系列的集合,我们都能通过迭代器来对集合中的元素进行遍历。

注意:我们可以在源码中追溯到集合的顶层接口,比如 Collection 接口,可以看到它继承的是类 Iterable

那这就得说明一下 Iterator 和 Iterable 的区别:

 Iterable :存在于 java.lang 包中。

 

我们可以看到,里面封装了 Iterator 接口。所以只要实现了只要实现了Iterable接口的类,就可以使用Iterator迭代器了。

 Iterator :存在于 java.util 包中。核心的方法next(),hasnext(),remove()。

 这里我们引用一个Iterator 的实现类 ArrayList 来看一下迭代器的使用:暂时先不管 List 集合是什么,只需要看看迭代器的用法就行了

 ②、Collection:List 接口和 Set 接口的父接口

    

  看一下 Collection 集合的使用例子:

由于 List 接口是继承于 Collection 接口,所以基本的方法如上所示。

1、List 接口的三个典型实现:

  ①、List list1 = new ArrayList();

    底层数据结构是数组,查询快,增删慢;线程不安全,效率高

   ②、List list2 = new Vector();

    底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合

   ③、List list3 = new LinkedList();

    底层数据结构是链表,查询慢,增删快;线程不安全,效率高

 怎么记呢?我们可以想象:

  数组就像身上编了号站成一排的人,要找第10个人很容易,根据人身上的编号很快就能找到。但插入、删除慢,要望某个位置插入或删除一个人时,后面的人身上的编号都要变。当然,加入或删除的人始终末尾的也快。

  链表就像手牵着手站成一圈的人,要找第10个人不容易,必须从第一个人一个个数过去。但插入、删除快。插入时只要解开两个人的手,并重新牵上新加进来的人的手就可以。删除一样的道理。

 2、除此之外,List 接口遍历还可以使用普通 for 循环进行遍历,指定位置添加元素,替换元素等等。

 ④、Set:典型实现 HashSet()是一个无序,不可重复的集合

1、Set hashSet = new HashSet();

  ①、HashSet:不能保证元素的顺序;不可重复;不是线程安全的;集合元素可以为 NULL;

  ②、其底层其实是一个数组,存在的意义是加快查询速度。我们知道在一般的数组中,元素在数组中的索引位置是随机的,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定的值时,需要把查找值和一系列的元素进行比较,此时的查询效率依赖于查找过程中比较的次数。而 HashSet 集合底层数组的索引和值有一个确定的关系:index=hash(value),那么只需要调用这个公式,就能快速的找到元素或者索引。

  ③、对于 HashSet: 如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

     1、当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置

    1.1、如果 hashCode 值不同,直接把该元素存储到 hashCode() 指定的位置

    1.2、如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals() 作比较

        1.2.1、hashCode 相同,equals 为 true,则视为同一个对象,不保存在 hashSet()中

        1.2.2、hashCode 相同,equals 为 false,则存储在之前对象同槽位的链表上,这非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

注意:每一个存储到 哈希 表中的对象,都得提供 hashCode() 和 equals() 方法的实现,用来判断是否是同一个对象

对于 HashSet 集合,我们要保证如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

常见的 hashCode()算法:

2、Set linkedHashSet = new LinkedHashSet();

  ①、不可以重复,有序

  因为底层采用 链表 和 哈希表的算法。链表保证元素的添加顺序,哈希表保证元素的唯一性

3、Set treeSet = new TreeSet();

  TreeSet:有序;不可重复,底层使用 红黑树算法,擅长于范围查询。

  如果使用 TreeSet() 无参数的构造器创建一个 TreeSet 对象, 则要求放入其中的元素的类必须实现 Comparable 接口所以, 在其中不能放入 null 元素

必须放入同样类的对象.(默认会进行排序) 否则可能会发生类型转换异常.我们可以使用泛型来进行限制

  

自动排序:添加自定义对象的时候,必须要实现 Comparable 接口,并要覆盖 compareTo(Object obj) 方法来自定义比较规则

    如果 this > obj,返回正数 1

    如果 this < obj,返回负数 -1

    如果 this = obj,返回 0 ,则认为这两个对象相等

           *  两个对象通过 Comparable 接口 compareTo(Object obj) 方法的返回值来比较大小, 并进行升序排列

定制排序: 创建 TreeSet 对象时, 传入 Comparator 接口的实现类. 要求: Comparator 接口的 compare 方法的返回值和 两个元素的 equals() 方法具有一致的返回值  

当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果

以上三个 Set 接口的实现类比较:

  共同点:1、都不允许元素重复

      2、都不是线程安全的类,解决办法:Set set = Collections.synchronizedSet(set 对象)

  不同点:

    HashSet:不保证元素的添加顺序,底层采用 哈希表算法,查询效率高。判断两个元素是否相等,equals() 方法返回 true,hashCode() 值相等。即要求存入 HashSet 中的元素要覆盖 equals() 方法和 hashCode()方法

    LinkedHashSet:HashSet 的子类,底层采用了 哈希表算法以及 链表算法,既保证了元素的添加顺序,也保证了查询效率。但是整体性能要低于 HashSet    

    TreeSet:不保证元素的添加顺序,但是会对集合中的元素进行排序。底层采用 红-黑 树算法(树结构比较适合范围查询)

  ⑤、Map:key-value 的键值对,key 不允许重复,value 可以

  1、严格来说 Map 并不是一个集合,而是两个集合之间 的映射关系。

    2、这两个集合没每一条数据通过映射关系,我们可以看成是一条数据。即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。

    3、因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。

  Map 的常用实现类:

  ⑥、Map 和 Set 集合的关系

    1、都有几个类型的集合。HashMap 和 HashSet ,都采 哈希表算法;TreeMap 和 TreeSet 都采用 红-黑树算法;LinkedHashMap 和 LinkedHashSet 都采用 哈希表算法和红-黑树算法。

    2、分析 Set 的底层源码,我们可以看到,Set 集合 就是 由 Map 集合的 Key 组成。

 

四、Java IO流

流的概念和作用

  • 流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象
  • 流的本质:数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
  • 作用:为数据源和目的地建立一个输送通道

JavaIO所采用的模型

  • Java的IO模型设计非常优秀,它使用Decorator(装饰者)模式,按功能划分Stream,您可以动态装配这些Stream,以便获得您需要的功能。
  • 例如,您需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

IO流的分类

  • 按数据流的方向分为 输入流、输出流
  • 此输入、输出是相对于我们写的代码程序而言,
  • 输入流:从别的地方(本地文件,网络上的资源等)获取资源 输入到 我们的程序中
  • 输出流:从我们的程序中 输出到 别的地方(本地文件), 将一个字符串保存到本地文件中,就需要使用输出流。
  • 按处理数据单位不同分为 字节流、字符流

  • 按功能不同分为 节点流、处理流
  • 节点流:以从或向一个特定的地方(节点)读写数据。如FileInputStream 
  • 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装
  • 4个基本的抽象流类型,所有的流都继承这四个
  •  输入流输出流字节流InputStreamoutputStream字符流ReaderWriter

  • 总结流的分类
    1. 首先自己要知道是选择输入流还是输出流,这就要根据自己的情况而定,如果你想从程序写东西到别的地方,那么就选择输出流,反之用输入流
    2. 然后考虑你传输数据时,是选择使用字节流传输还是字符流,也就是每次传1个字节还是2个字节,有中文肯定就选择字符流了
    3. 前面两步就可以选出一个合适的节点流了,比如字节输入流inputStream,如果要在此基础上增强功能,那么就在处理流中选择一个合适的即可
  • 字符流的由来: Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表

IO流的特性

  1. 先进先出,最先写入输出流的数据最先被输入流读取到
  2. 顺序存取,可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile可以从文件的任意位置进行存取(输入输出)操作
  3. 只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流

IO流常用到的五类一接口

  • 在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了。

Java IO流对象

1.  输入字节流InputStream

  • 认识每个类的功能即作用
  • ByteArrayInputStream:字节数组输入流,该类的功能就是从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去,我们拿也是从这个字节数组中拿
  • PipedInputStream:管道字节输入流,它和PipedOutputStream一起使用,能实现多线程间的管道通信
  • FilterInputStream :装饰者模式中处于装饰者,具体的装饰者都要继承它,所以在该类的子类下都是用来装饰别的流的,也就是处理类
  • BufferedInputStream:缓冲流,对处理流进行装饰,增强,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送。效率更高
  • DataInputStream:数据输入流,它是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
  • FileInputSream:文件输入流。它通常用于对文件进行读取操作
  • File:对指定目录的文件进行操作,具体可以查看讲解File的博文。注意,该类虽然是在IO包下,但是并不继承自四大基础类
  • ObjectInputStream:对象输入流,用来提供对“基本数据或对象”的持久存储。通俗点讲,也就是能直接传输对象(反序列化中使用

2.  输出字节流OutputStream

  • OutputStream 是所有的输出字节流的父类,它是一个抽象类
  • ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据
  • ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流(序列化中使用)

3.  字符输入流Reader

  • Reader 是所有的输入字符流的父类,它是一个抽象类
  • CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。PipedReader 是从与其它线程共用的管道中读取数据
  • BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象
  • FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号
  • InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系

4.  字符输出流Writer

  • Writer 是所有的输出字符流的父类,它是一个抽象类
  • CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据
  • BufferedWriter 是一个装饰器为Writer 提供缓冲功能
  • PrintWriter 和PrintStream 极其类似,功能和使用也非常相似
  • OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似

5.  字节流和字符流使用情况:(重要)

  • 字符流和字节流的使用范围:字节流一般用来处理图像,视频,以及PPT,Word类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件

字符流与字节流转换

  • 转换流的作用,文本文件在硬盘中以字节流的形式存储时,通过InputStreamReader读取后转化为字符流给程序处理,程序处理的字符流通过OutputStreamWriter转换为字节流保存
  • 转换流的特点:
    1. 其是字符流和字节流之间的桥梁
    2. 可对读取到的字节数据经过指定编码转换成字符
    3. 可对读取到的字符数据经过指定编码转换成字节
  • 何时使用转换流?
    1. 当字节和字符之间有转换动作时
    2. 流操作的数据需要编码或解码时

字节流和字符流的区别(重点)

  • 字节流和字符流的区别
    • 字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法
    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节
    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据
    • 结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节

五 Java线程、异常及网络编程

这里就不写了,线程跟网络编程的话一两句话写不完,篇幅所限,有时间的话我再单独写一篇关于线程和网络编程的。

六、Java架构师学习核心书单

除了以上列出的主要一些主要分类,这里再补充一些。

  • 容器推荐《Docker从入门到事件》《Kubernetes 权威指南》;
  • Maven推荐《Maven实战》;
  • Git推荐《ProGit》;
  • Linux自然是《鸟哥的Linux私房菜》;
  • Elasticsearch推荐《Elasticsearch实战》;
  • 设计模式推荐《设计模式之禅》。

以上推荐的书籍我都收集了电子档,学生党或者手头紧的朋友,可以看我首页

  • 22本Java架构师核心书籍

就写到这吧,头发掉的差不多了,如果能点个赞加个关注那真是再好不过的事了,下篇文章见

end

小讯
上一篇 2024-12-29 13:10
下一篇 2024-12-28 08:32

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/10611.html