JAVA

1.JAVA概述

1.什么是编程语言?

开发于sun公司,现属oracle

人与计算机沟通交流的一种特殊语言

詹姆斯·高斯林 java语言创始者

 

配置环境变量:

jdk java开发工具包

jre java运行时环境

jvm java虚拟机

java程序执行过程:

1.先编写java源代码 javac.exe

2.对java源代码进行编译 javac 源文件名称

编译完成后会生成当前源文件所生成的字节码文件.class

字节码文件是开发人员看不懂的, 只有jvm虚拟机才能识别

3.运行java代码 java.exe

java 类名

 

java语言是支持跨平台的:用java编写的源代码可以在不同的系统 上运行

使用JDK开发完成的java程序,交给JRE去运行

 

2.java入门

java语法规范

1.类和接口要声名一个标识符用大驼峰命名法

2.变量和方法要声名标识符用小驼峰命名法

注释:

编译器中按住alt可以选择一列

#快捷键 ctrl + shift + /

单行注释://注释文字

多行注释:/* 注释文字 */

文档注释:/** 注释文字 */

 

常量

在程序执行的过程中其值不可以发生改变

在方法外面定义,全大写,单词与单词之间用下划线分隔开

 

 

java是一门强类型语言,对于每一种数据都定义了明确的具体数据类型

在内存总分配了不同大小的内存空间

 

数据类型

1.基本数据类型:

a.数值型:整数类型(byte,short,int,long

浮点类型(float,double

b.字符型:(char)

c.布尔型:(boolean)

2.引用数据类型:

a.类: (class

b.接口:(interface

c.数组: ([ ]

 

有几个数据类型关键字就开辟几份内存空间

强制转换:

运算符+ - * / %(取余)

变量

成员变量:实例变量,静态变量

在JVM虚拟机的栈内存开辟空间,但静态变量在方法区空间开辟内存

局部变量:方法体内部的变量(包括参数中的变量)

 

 

JAVA对类的定义:

java本身是面向对象的语言,如果需要使用java去开发一个软件**

那么,软件中的每一个模块都是一个个体,每一个个体会拆分为很多单元,每个单元本身

是一个模糊的概念,针对这样的一个概念,我们一般用类去定义,然后在具体执行的时候,

需要让模块之间相互协作,正常的去模拟现实生活中场景的时候,就需要创建这个类

所对应的实例

将鼠标光标放置于未被导入的包名称上,按住alt + Enter 可以快速import包 (IntelliJ IDEA支持)

 

创建类

new关键字 创建类的实例

创建类实例的对象

对象.调用方法

 

方法

方法是完成特定功能的代码块

格式:

 

 

方法特性:

递归:

使用递归会严重损耗系统资源,因此要尽量避免使用递归

方法的重载:

在一个区间内,java语法中运行方法名称相同,参数列表不同的语法格式,这种现象我们称之为重载

 

可变长参数

在参数中的位置只能出现在最后 格式:数据类型...

可变长参数本质其实就是一个数组

可变长参数数值可以传也可以不传,也可以传多个

 

 

数组

new创建的容器存放在堆内存中

数组是一个引用数据类型

可以通过new的方式去创建数组

在不赋值的情况下,取到的是默认值,整型默认值是0

数组操作常见的小问题:

 

 

字符串

字符串常用函数

 

3.面向对象(万物皆对象)

面向对象特征:

类与对象关系

 

以后我们开发项目的时候,要不断的去声明类,在类中需要去封装属性和行为,

但是针对某一些属性,我们需要将它保护起来,不希望直接对外界提供访问。

所以我们会将这些属性用private关键字进行修饰,private代表的是私有的访问权限修饰符。

被private修饰的属性只能在本类中才能访问到

 

alt + INS键快速选择创建类的get方法与set方法

 

JVM内存分配

栈内存:主函数,定义的普通函数,基本数据类型,数据地址(引用数据类型的地址),所有的方法

堆内存:new出来的对象,应用数据类型的值

方法区内存:方法区内存中一般存放的是代码片段(java源文件编译之后的字节码文件),以及常量池

静态方法存在栈内存中,静态变量存在于方法区内存中

局部变量和实例变量的区别:

局部变量是存在于方法体内部的变量(包含了参数),局部变量如果是基本数据类型,则直接在栈中开辟空间,如果是引用数据类型,变量声明部分在栈中,实例创建部分在堆中,如果是实例遍历,所有的内存都会在堆中去开辟。

 

常量池:

常量池一般存储字符串常量

 

Java中通过 == ,如果比较的是基本数据类型,比的是值,如果比较的是引用数据类型,则比较的是内存地址

 

 

private关键字:私有的

是一个权限修饰符

可以修饰成员(成员变量和成员方法)

被private修饰的成员只能在本类中才能访问

this关键词

this:代表所在类的对象的引用(方法被哪个对象调用,this就代表那个对象)

this.变量名 访问的是本类中的实例变量

 

构造方法(Constructor)

作用:给对象的数据进行初始化

方法名与类名相同,没有返回值类型,连void也没,没有具体的返回值

如果不提供构造方法,那么系统会给出默认的构造方法,如果提供了构造方法(不管是否带参数),系统将不再提供默认无参数的构造方法

构造方法可以重载

 

构造方法的作用

无参构造:实例化对象,但是实例化对象后,所有的属性都是没有被赋值的

有参构造:实例化对象,可以在创建对象时,就给实例的属性赋值

 

静态关键字 static

可以修饰方法,可以修饰属性,但是不能修饰类

被静态关键字修饰的属性和方法,会成为一个类级别的元素,会随着类的加载而加载

 

局部变量不能被static修饰

 

被static修饰的变量我们称之为静态变量

 

静态变量会随着类的加载而加载,在JVM解析字节码文件时,该变量的内存空间就会被开辟出来

静态变量的内存空间会被开辟在该方法区中

 

静态方法

 

静态代码块:

在类加载的时候将会被执行的语句体,而且只加载一次(在主函数执行前执行)

 

一个类基本类的标准代码写法

 

继承

概述:多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

通过extends关键词可以实现类与类的继承

class 子类名 extends 父类名 {}

 

单独的这个类称为父类,基类,或者超类;这多个类可以称为子类或者派生类

###

继承可以让子类去继承父类,这样子类会继承父类所有的属性和方法,在java中是不允许有多继承

 

Object类:是java中的祖宗类,当一个类在代码层面没有去继承任何的父类,那么这个类

默认会继承Object类

 

继承的重写(覆盖):

子类继承父类以后,可以获取到父类所有的方法(包含被private修饰的私有方法,但是访问不到)

如果父类方法的功能不能够满足子类的需求,这个时候,子类可以针对这个方法进行重写

父类中私有方法不能被重写

子类重写父类方法时,访问权限不能更低

父类静态方法,子类也必须通过静态方法进行重写

 

继承的好处:

提高了代码的复用性

提高了代码的维护性

让类与类之间产生了关系,是多态的前提

 

不要为了部分功能而去继承继承中类之间的体现是“is a”的关系

 

继承中成员变量的关系

在子类方法中访问一个变量:

 

Super关键字

子类继承父类之后,子类可以通过super关键字去指向父类的元素(属性,普通方法,构造方法)

如果指向父类的构造用super() 如果指向子类的构造用 this()

 

继承中的构造方法的关系

子类中所有的构造方法默认都会访问父类中的空参数的构造方法

因为子类会继承父类中的数据,可能还会使用父类的数据

所以,子类初始化之前,一定要先完成父类数据的初始化

每一个构造方法的第一条语句默认都是:super()

 

方法重写与方法重载的区别?方法重载能改变返回值类型吗?

答:方法重写建立在类的继承关系上,是对父类同名方法的覆盖。方法重载是一个类中,多个同名方法,但是参数列表不同。方法重载可以改变返回值类型。

 

 

多态

某一事物,在不同时刻表现出来的不同状态

多态是面向对象特征之一,也是最重要的一个特征

多态思想:

为了解除类与类之间的耦合(模块与模块之间的耦合),不能面向具体编程,而要面向抽象编程(面向接口编程)

 

首先多态必须要有父子关系的体现(继承,实现),是在不同时刻展现出不同的状态

 

父类的引用接收子类对象(向上转型),或者是向下转型都是多态的体现

 

多态在程序执行时分为编译阶段和运行阶段:

普通方法:

编译阶段看左边:编译时要检查调用的方法在父类中是否存在

运行阶段看右边:因为我们实际new出来的对象是子类对象,所以最终执行的方法也是子类的方法

静态方法:

静态方法可以用类名去调用,类名.静态方法名()

静态方法也可以用实例去调用,对象引用.静态方法名(),本质也是通过类名去调用

静态方法是一个类级别的方法,所以它已经脱离了实例,和对象没有关系

编译看左边,运行看左边

多态前提和体现

有继承关系

有方法重写

有父类引用指向子类对象

 

多态在开发中的作用

主任喂养宠物的案例:

有一个主人是master喜欢喂养宠物,喂养的宠物有很多种(猫,狗,兔子,鸟),然后随着时间的推 移,主人会喂养越来越多的宠物,针对于这样的场景我们通过java程序去模拟一下(体现出多态)

 

重载算不算多态?不算多态(没有体现父子关系)

 

结论:如果按照传统的方式进行开发(不使用多态):会发现程序进行迭代的时候,需要去修改主人类(需要在原有的代码继承上进行修改),这样做违背了设计模式中的开辟原则(OCP 对扩展开发,对修改关闭):在写程序的时候,尽量避免在原有的代码之上进行修改,这样的话修改的部分容易对别的类(或者说别的模块造成影响)

 

抽象类

一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类

 

抽象类是java中最顶级的一种表现形式,接下来就是类,再接下来就是对象

 

抽象类用abstract修饰 > 类class > new 对象

 

我们如果想创建对象的话,只能通过类class去创建,抽象类是不能创建对象的

因为抽象类的下一级是类

 

格式:abstract class 类名{}

 

 

抽象类中是有构造方法的(抽象类不可以实例化对象,但是却存在构造函数)

抽象类父类构造函数是通过子类构造去调用的,通过super关键字调用父类构造

 

final关键字

final关键字:可以修饰类,成员变量,局部变量,方法(静态方法,实例方法)

 

用final关键字能不能修饰抽象类?不可以,因为抽象类的作用就是用来被子类继承的,但是被final修饰的类是无法被子类继承的,final和abstract是不能同时出现的

 

如何去声明一个常量?

常量是程序运行过程中不可以发生改变的量

对于那些无论创建多少次对象,值都亘古不变的数据,我们把该属性定义为常量

用static修饰常量是因为想让改常量变成一个类级别的,然后随着类加载,该常量就初始化

避免多次开辟空间,浪费系统资源

 

用final修饰常量,是因为不希望在程序运行时,该量的值发生变化,final代表最终的

接口

接口:接口是一种规范,在进行程序设计的时候,都是基于接口进行开发

因为在声明方法的时候,我们需要这个方法存在多变性,而且可以随着业务的改变,针对这个接口做具体的实现

构造方法:没有,因为接口主要是扩展功能的,而没有具体存在

接口的定义

格式: interface 接口名 {}

 

接口在开发中的作用:

设计模式中提到在设计程序时,我们要面向接口开发,接口+多态,可以实现程序的可拓展性,灵活性

 

implements关键字:

作用:子类实现接口的关键字(类似extends关键字)

接口中定义的抽象方法,在子类实现之后一定要进行重写

格式: class 类名 implements 接口名{}

 

接口案例

按照多态的方式,由具体的子类实例化,这其实也是多态的一种,接口多态

 

类对多个接口方法的实现

接口与接口之间的多继承

 

总结:抽象类与接口的区别:

抽象类有成员变量,以及抽象方法(没有方法体)与普通方法,拥有自己的构造函数,继承抽象类的子类需要重写抽象类中的所有抽象方法,抽象类没有实例对象,因为其下一级是类

抽象类被继承体现的是”is a“的关系,共性功能

接口可以说是特殊的抽象类,只是接口内只有抽象方法和常量,接口不具有构造函数,实现接口的类需要重写接口中的所有抽象方法,接口没有实例对象,它的作用是用来被子类实现的

接口被实现体现的是:”like a“的关系,扩展功能

类与类,类与接口,接口与接口之间的关系

类与类:

继承关系,只能单继承,但是可以多层继承

类与接口:

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

接口与接口之间:

继承关系,可以单继承,也可以多继承

 

接口和抽象类都可以作为一个方法的返回值,参数,也可以作为类的成员变量

 

package只能存在工程的第一行

包其实就是文件夹

作用:对类进行分类管理

 

格式:package 包名;

多级包用”.“分开即可

 

访问权限修饰符区别

访问权限修饰符本类同一包下子类(同一包下)子类(不同包下)不同包下非子类
private××××
default(不写修饰符)××
protected×
public

 

内部类

把类定义在其他类的内部,这个类就被称为内部类

内部类的访问特点:

内部类可以直接访问外部类的成员,包括私有

外部类要访问内部类的成员,必须创建对象

内部类中不允许写静态方法

 

匿名内部类

就是内部类的简化写法

前提:存在一个类或接口

这里的类可以是具体类也可以是抽象类

格式:

new 类名或者接口名( ){重写方法;}

本质:

创建一个父类所对应的匿名对象

是一个继承了

 

类或者实现了接口的子类匿名对象

 

4.异常

当程序出现异常时,java会创建对应的异常对象,并且抛出(throw)

当异常抛出之后,会影响接下来代码的执行(打断)

Throwable类

在 Java 中,所有的异常都有一个共同的祖先类Throwable(可抛出)

Throwable有两个子类 Error 和 Exception

 

Error类

错误(Error): JVM系统内部错误,资源耗尽等严重情况

Error是Throwable的子类,用于指示合理的应用程序不应该试图捕获的严重问题

无法从代码层面解决此异常

 

Exception类

异常(Exception): 其它因编程错误或偶然的外在因素导致的一般性问题

Exception(异常):是程序本身可以处理的异常,Exception 类有一个重要的子类RuntimeException

常见异常:

RuntimeException

ArithmeticException:数学计算异常

NullPointerException:空指针异常

NegativeArraySizeException:数组索引越界异常

ClassNotFoundException:类文件未找到异常

ClassCastException:造型异常

IOException

FileNotFoundException:文件未找到异常

EOFException:读写文件尾异常

异常:

编译时异常:当JVM进行编译的时候就会出现报错,对于那些大概率会发生的异常,我们会定义成编译时异常,编译时异常要求开发人员必须对其进行处理

直接或间接继承了Exception类的异常就是编译时异常

 

运行时异常:程序在运行时,才会出现的异常,小概率会出现的异常,我们会定义成运行时异常,开发人员可以不用对其进行处理

直接或者是间接的继承了RuntimeException类,该异常就是运行时异常

 

异常处理机制

1.通过throws关键字将异常往上抛

 

2.通过异常的捕获try{}catch{}语句块

 

异常处理的try - catch - finally语句

语句块机制:

注意:用try{…}catch{…}捕捉了异常之后一定要对在catch{…}中对其进行处理,那怕是最简单的一句输出语句,或打印日志来显示整个调用流程

 

throws关键字

throws : 是方法可能抛出异常的声明

用在声明方法时,表示该方法可能要抛出异常,交给上层调用它的方法程序处理

通过throws关键字(写在方法后面的)把异常向上抛(抛给该方法的调用处)

不会影响程序正常运行

 

自定义异常

java给我们提供了很多种异常,但是在做项目的时候,这些异常是不够用的,所以需要我们自定义异常

自定义异常方式:

  1. 直接声明一个类
  2. 如果想定义成编译时异常让该类去继承Exception
  3. 如果想定义成运行时异常,让该类去继承RuntimeException
  4. 然后需要提供一个构造,通过super关键字,把异常信息告诉父类即可

throw关键字

系统在遇到异常时会自动生成一个 throw new 异常类(); 来抛出异常

throw是语句抛出一个异常,一般是在方法体的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常

字符串比较的方法:

 

空指针异常时以后我们写程序经常会遇见的一种异常,接收过来的引用为空,然后拿着该引用去调用方法

空指针异常的解决方案:我们要对接收过来的引用数据类型做非空校验

 

5.集合

集合:

JAVA中的自动装箱和自动拆箱:

java中所有的基本数据类型都有对应的封装类(引用数据类型),我们学习的集合是只能存放引用数据类型的容器。

Java中有自动装箱机制,会把基本类型转化为其对应的封装类:

 

将引用数据类型转为基本数据类型就是自动拆箱

 

Collection

Iterator接口:Collection继承的超级接口, 只有一个返回迭代器的方法

Collection : 集合层次中的根接口,JDK没有提供这个接口直接的实现类

将一组对象以集合元素的形式组织到一起,在其子接口中分别实现不同的组织方法

Collection的两个子接口:

1.List接口:

有序集合的三种访问方式

是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式

迭代器方法访问元素:

Iterator接口定义了对Collection类型对象中所含元素的遍历等增强处理功能

可以通过Collection接口中定义的iterator()方法

获得一个对应的Iterator(实现类)对象

Set(实现类)对象对应的Iterator仍然是无序的

List(实现类)对象对应的ListIterator对象可以实现对所含元素的双向遍历:

使用next()方法和previous()方法

forEach循环(加强版for循环):

 


 

案例分析(关于集合当中的方法):

List的方法:

contains 方法: 底层重写了equals方法,判断集合中是否包含某一个元素,如果该元素重写了equals方法,比较的就是内容,如果没有重写equals方法,默认会采用Object类中的equals方法,这时候比较的就是地址

remove方法: 底层重写了equals方法,删除集合中的元素,但是永远只能删除一个元素,因为在底层删除之后就return了

 


ArrayListvector 集合区别:

这两个集合底层都封装了数组这一种数据结构,ArrayList是线程不安全的,Vector是线程安全的

所有封装的方法都是一样的,还有就是扩容机制有一点不一样,ArrayListvector最开始的时候只能存放10个元素,默认初始容量是10,当元素超过10个之后,就会触发底层的扩容机制

ArrayList的扩容因子是1.5 10 * 1.5 = 15

Vector的扩容因子是2.0 10 * 2.0 = 20

 

ArrayListLinkedList 集合区别:

ArrayList 底层是数组,LinkedList 底层是链表结构

数组查询效率高,随机增删效率低

链表随机增删效率高,查询效率低

 

集合的泛型

JDK1.5版本之后引入的新特性,强制让集合只能存放某一种数据类型

<数据类型>钻石表达式

自定义泛型:

2.Set接口:

Set接口的实现类:HashSet

底层封装了HashMap

HashSet不允许出现重复元素,不保证集合中元素的顺序(存入的顺序与取出的不同

HashSet中允许包含值为null的元素,但最多只能由一个null元素

Set接口的实现类:TreeSet

底层封装了TreeMap

 

Map接口:

  1. map集合是存放键值对的容器

  2. value是key的一个替代品,我们以后操作元素时,是根据key去操作

  3. map集合的key是不允许重复的,如果在添加元素时,key的值重复,那么后添加的元素将会把前一个覆盖掉

  4. value的值可以重复

     

Map:包含了key-value对, Map不能包含重复的key, SortedMap是一个按照升序排列key的Map

 

映射(map)是一个存储关键字和值的关联或者说是关键字/值对的对象,给定一个关键字,可以得到它的值

关键字和值都是对象,关键字必须是唯一的,但值是可以被复制的

有些映射可以接收null关键字和null值,而有的则不行

 

 

如何遍历Map集合(两种方法):

1.keySet方法:将map集合所有key部分转成set集合,再根据get()获取对应value的值

2.Set<Map.Entry<K,V> entrySet()

将map集合转成set集合(效率比第一种高)

Map接口实现类:HashMap:

hashMap集合的存储过程:

hashCode()随机生成哈希码值

我们添加到集合的元素是引用数据类型

那一定会继承Object类中的hashCode方法

首先map集合会调用put()方法来添加元素,

这个时候hashMap底层会生成hash值(如果在该引用数据类型中重写了hashCode方法,那么生成的不再是随机值,数据便会存储在同一单元中)

根据这个hash值来计算该元素的位置应该存放在数组的哪个单元下面

确定了存放的单元,这个时候就会去拿着key的引用

与该单元下所有的node节点进行一个equals比较(该引用数据类型有没有重写equals方法如果重写了,比较的是对应属性的内容,如果没有重写,那比较的就是内存地址)

如果发下equals的返回值是true,新添加进来的节点将会把旧的节点覆盖掉

如果equals的返回值是false,该节点将会存放在数组单元中的单向链表的最下面(成为链表尾节点)

 

hashMap案例:

 

Map接口实现类:HashTable:

HashTableHashMap的区别:

HashMap key-value可以为一次空

HashTable 不可以为空

HashTable是线程同步的,也是线程安全的

其实hashtable就是线程安全版的hashmap,所有的方法以及使用方式都是一样的

hashtable的key和value不允许为空

arrayListvector是一样的,hashmap的执行效率要比hashtable高,因为hashtable所有的方法都添加了线程同步关键字,这样的话在多线程场景下,执行效率是不高,但是以后开发中我们可以通过别的途径来解决线程安全问题,所以hashMap这个集合是我们以后使用的最多key-value集合

Map接口实现类:TreeMap:

treeMap是一个可以按照key自动排序的集合

 

案例:

hashMap统计字符出现的个数

 

Properties集合:

继承了hashtable

集合总结

Collection体系:

List接口:

ArrayList:

有序可重复的集合,底层封装了数组这种数据结构,所以有数组所有的特性,可以通过下标索引来获取元素,查询数据的效率会很高,但是随机增删的效率会比较低,本身是一个线程不安全的集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是1.5,每触发一次扩容机制,加载为原来的1.5倍

Vector:

Vector就是一个线程安全版的Arraylist,这个集合的所有方法都添加了线程同步关键字,在多线程场景下,效率是很低的,我们一般不会使用这个集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是2.0,每触发一次扩容机制,加载为原来的2倍

LinkedList:

这个集合在底层封装了单向链表这种数据结构,所以该集合拥有链表的所有特性,查询效率是不高的,但是随机增删效率很高,而且在LinkedList底层还为我们封装了队列,我们可以去调用入队和出队的方法

Set接口:

HashSet:

在底层为我们聚合了HashMap,在添加元素的时候,实际上调用了HashMap的put方法,元素存放在put方法的key部分

HashSet是一个无序不可重复的集合,存入数据的顺序和取出数据的顺序是不同的

TreeSet:

在底层为我们聚合了TreeMap,在添加元素的时候,实际上调用了TreeMap的put方法,元素存放在put方法的key部分

TreeSet是一个可以按照排序规则自动排序的方法,如果存入元素的引用时我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常

 

Map体系:

HashMap:

可以以key-value的形式去存放数据,key和value都是允许为空的,不是线程安全的集合,但是执行效率很高,在底层封装的是哈希散列

HashTable:

线程安全版的HashMap,执行效率不是很高,key和value都不允许重复

TreeMap:

是可以根据key来完成自动排序的key-value集合,如果存入元素的引用是我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常

Properties:

这个集合继承了HashTable,所以该集合是一个线程安全的集合,而且提供了私有的方法SetProper(),getProper(),该集合可以集合流对象高效的去读取配置文件,把数据解析到集合中