Java基础

JVM、JRE、JDK 有什么关系
JVM、JRE、JDK 有什么关系
JDK(Java Development Kit)是用于开发 Java 应用程序的软件环境。里面包含运行时环境(JRE)和其他 Java 开发所需的工具,比如说解释器(java)、编译器(javac)、文档生成器(javadoc)等等。

JRE(Java Runtime Environment)是用于运行 Java 应用程序的软件环境。也就是说,如果只想运行 Java 程序而不需要开发 Java 程序的话,只需要安装 JRE 就可以了。

JVM (Java Virtual Machine) ,也就是 Java 虚拟机,由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成,屏蔽了不同操作系统(macOS、Windows、Linux)的差异性,使得 Java 能够“一次编译,到处运行”。

比如说我用 macOS 生成了一个 jar 包(里面是打包好的字节码——可以在 Java 虚拟机上运行的目标代码),可以丢给 Windows 用户直接运行,也可以直接上传到 Linux 服务器运行。

Java语法基础

字符串(String)

与其他编程语言不同,Java中的字符串不是基本类型(比如int、char等)。相反,所有字符串都是预定义的名为String的类的对象。例如,

//创建一个字符串
String type = "java programming";

字符串方法:(按使用频率排序)

equals():判断两个字符串是否相等,用法str.equals(s);

length():返回字符串长度。

valueOf():讲其他基本类型转化为字符串,是静态方法,直接通过String.valueOf()调用。

getBytes():将字符串转化为字符数组,

charAt(): 返回目标位置的字符

indexOf():返回字符串中指定字符的位置。

replace():将指定的旧字符替换为新字符。

toLowerCase():将字符串全部小写化

toUpperCase():将字符串大写

subString():获得指定位置子串

concat():拼接两个字符串

split(String reg):分割字符串,返回分割后的字符串数组。

数据类型转换

从小往大是兼容的
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
//但是小往大是会报错不行的;自己的例子:

int result;
int n1 = 2;
float f1 = 3.14f;
result = n1 * f1;

解决方案:(int)n1*f1或者float result;

Integer和int

Java中的数据类型两种:
基本数据类型: byte,short,int,long,float,double,boolean,char,
引用数据类型:类,数组,接口
Java为每个基本数据类型提供了封装类:Byte,Short,Integer,Long,Float,Double,Boolean,Character
为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每 一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱拆箱操作,使得二者可以相互转化。
integer是int的包装类,必须实例化后才可以使用:Integer a = new Integer(2);
当数字在-128-127时integer有一个缓存池,作用是让这些常用的数字占用更少的内存,所以当使用Integer.valueof(12)时这个时候时取得缓存池中的数据,没有新建一个integer对象,因此无论这样创建几个integet对象都是相等的,但是使用new integer创建的是真实的对象,是不一样的。

Lambda表达式(args…)->{operation}

JAVA中最重要的内容之一:
使得java中的函数编程思想前进了一大步,通过(args…)->(operation)实现不关注于谁实现了方法,而关注于方法本身,不需要像面向对象编程一样必须需要创建对象再使用对象的方法可以一些表达式中直接使用lambda实现操作。

泛型不适用基本数据类型,

Stream流

好像一个过滤器,有中间操作和最终操作;
01. 创建流:

//如果是数组的话可以使用Arrays.stream或者stream。of
public class stream(){
public static void main(){
String[] s = new String(){"one","two"};
//create stream
//Tips:Stream<type>
Stream<String> stream = Arrays.stream(s);
stream = Stream.of(s);
//如果是容器的话:可以直接使用stream方法来创建流
List<String> ls = new ArrayList<>();
ls.add("Pantene");
ls.add("zakel");
Stream<String> streamls = ls.stream();
}
}

助记:所有的都可以直接.stream()实现;
Reason:容器集合有自带的Stream()方法,直接调用就可以。对于数组来说,可以看作Arrays,所以需要调用Arrays.stream()其实大同小异。

  1. 操作流:

HashMap

整个遍历方式:

for(Map.Entry<Integer,Integer> entry = map.entrySet()){
//直接ForEach:
System.out.println(entry.getKey() + entry.get())
}
//foreach every Key or Value:
for(auto key : map.keySet){...}
for(auto value : map.values()){...}
//Lambda
map.forEach((k,v)->System.out.println("key"+k "value"+v));

Java面向对象编程

继承

通过extends实现继承,子类可以继承父类的属性和方法,但是不能继承构造方法。
可以使用super关键字来调用父类的构造方法。
通过@Override注解来标识重写父类的方法。
final关键字:

  1. 可以用来修饰类、方法、变量(成员变量和局部变量)
  2. final表示修饰的类或方法是继承树的末端,无法被继承/重写。
  3. final修饰的变量是常量,只能被赋值一次,赋值后不能再改变。
  4. final修饰的方法不能被重写。

覆盖的规则:

  1. 子类方法的参数列表和返回值类型必须与父类方法的参数列表和返回值类型相同。
  2. 子类方法的访问权限不能低于父类方法的访问权限。

区别于重载

  1. 重载是在同一个类中,方法名相同,参数列表不同。
  2. 重写是在子类中,方法名相同,参数列表相同。
  3. 重载是编译时多态,重写是运行时多态。

多态

多态(Polymorphism),字面意思是“多种形态”。在Java中,它指的是 允许不同类的对象对同一消息做出不同的响应。简单来说,就是 同一个方法调用,作用于不同的对象,会产生不同的执行结果。多態的核心在于 运行时绑定:程序在运行时,才动态地决定到底要调用哪个对象的方法。
多态的三个必要条件:

  • 继承:必须有类之间的继承关系。
  • 重写:子类必须重写父类的方法。
  • 父类引用指向子类对象:父类类型 变量名 = new 子类类型();

抽象abstract

如果不想让某个类被初始化,就以abstract关键词将其标记为抽象的。
抽象类是用abstract关键字修饰的类:public abstract class Animal{},抽象类中可以有抽象方法和非抽象方法,抽象方法是用abstract关键字修饰的方法,抽象方法没有方法体,只有方法声明,抽象类不能被实例化,只能被继承。
抽象方法的作用是为了让子类去实现,抽象类的作用是为了让子类去继承。
举个例子,什么时候我们需要用到抽象类:
从面向对象设计的思想出发,如果我们有时候需要定义一个动物类,以及这个类下面的众多子类,比如宠物、禽兽等,再往下还有具体的猫、狗、狮子、河马等,对于这些具有实体的动物也是我们需要具体实现的类,但对于一些抽象的类,比如上层的动物,宠物这些,我们并不希望他们可以创建实体出来,只希望他们能够在上层实现一些动物通用的方法供给子类去继承使用或实现。

接口interface

接口就好像是100%的纯抽象类,是用interface关键字修饰的类``,接口中只能有抽象方法和常量,接口不能有构造方法,接口不能被实例化,只能被实现。
一个类只能继承一个父类,但是可以实现多个接口。
接口的定义:

public interface pet{
public abstract void eat();
//接口的方法一定是抽象的,所以必须以分号;结尾
public abstract void sleep();
}

接口的实现:public class cat implements pet{}
接口的作用&使用思想:
举个例子:
比如动物类别下面有很多不同环境的动物,比如狮子,猫,狼,鲸鱼等,如果我们按照生物学上的分类的话,那么猫和老虎可能是继承自一个父类,但是现实中的某些动物比如猫、狗、松鼠等又可以归为一个宠物类别,这些不同属于一个父类,但是又具有统一的宠物行为,比如和主人互动,投喂等,在一个类只能继承一个父类的基础上我们再想统一这些不同的动物的行为,所以我们就可以使用接口来实现,我们可以定义一个public interface pet{}然后里面定义了一些宠物的行为,之后让猫狗鼠implements pet,之后就可以在每个具体的类中实现接口中抽象的方法了。(最重要的结果)继而我们可以在后面如果需要用到这些宠物的行为的话就可以使用类似于pet cat1 = new cat()构建一个pet类型,但是指向的是cat对象,因此就可以直接调用例如cat.sleep方法

因为有着实现接口必须按照接口定义来的特性,所以我们有一句话:接口 定义 规范,就是这么得来的,很多规范可以在接口中确定,让后续加进来的类/对象必须按照要求实现。

静态方法

静态方法无需创建对象就可以调用对象的静态方法,用static修饰。
静态方法无法调用非静态方法及变量。
可以把static修饰的方法或变量看作是属于类的属性或方法,而不是属于这个类的任何一个实例对象的属性或方法。

  • static修饰的变量,也称为类变量,是指在类中使用static关键字修饰的变量,静态变量在类加载时就被初始化,且只被初始化一次,静态变量可以通过类名直接访问,也可以通过对象名访问。常用于统计对象初始化次数等。

  • 在一个类中,static {} 这种语法结构被称为 静态初始化块 (Static Initialization Block),或者常被叫做 静态代码块 (Static Block)。
    静态代码块是属于类本身的、用于初始化类级别资源的一段代码。它不属于任何一个对象实例。
    它的主要特点是:在Java虚拟机(JVM)加载该类时执行,并且只执行一次。
    因为静态代码块是在任何对象实例创建前运行,因此

    • 它只能访问类中的静态成员
    • 在静态代码块中,不能使用this关键字,因为this关键字是指向当前对象的引用,而静态代码块是在类加载时执行的,此时对象还没有被创建,所以不能使用this关键字。

Java构造器

创建对象有三个步骤:

  1. 声明引用变量:Animals cat
  2. 创建对象new Cat()
  3. 连接对象与引用Animals cat = new Cat()

在new创建对象时会调用对象的构造函数,如果已经写了一个有参数的构造函数,那么必须还需要写一个没有参数的构造函数。
当类继承了一个父类时,该类的构造函数会自动调用父类的构造函数,如果想要手动调用父类的构造函数可以使用super()方法,且必须置于第一行。
使用this()从某个构造函数调用另一个构造函数,this()必须置于第一行,且只能调用一个构造函数。

★容器(集合框架)

Java Collection接口及其子接口。

如上图可见,Collection接口是集合框架的根接口,其下面还有三个子接口分别是List,Set和Queue。

Collection 方法

Collection 接口包括各种可用于对对象执行不同操作的方法。这些方法在其所有子接口中均可用。

  • add() - 将指定的元素插入到集合中
  • size() - 返回集合的大小
  • remove() - 从集合中删除指定的元素
  • iterator() - 返回一个迭代器以访问集合的元素
  • addAll() - 将指定集合的所有元素添加到集合中
  • removeAll() - 从集合中删除指定集合的所有元素
  • clear() - 删除集合中的所有元素

List

由于List是接口,因此无法从中创建对象。为了使用List接口的功能,我们可以使用以下类:

![image-20251028161842633](/Users/mag1code/Library/Application Support/typora-user-images/image-20251028161842633.png)

List方法

List接口包括Collection接口的所有方法。 这是因为Collection是List的超级接口。

Collection接口中还提供了一些常用的List接口方法:

  • add() - 将元素添加到列表
  • addAll() - 将一个列表的所有元素添加到另一个
  • get() - 有助于从列表中访问元素
  • iterator() - 返回迭代器对象,该对象可用于顺序访问列表的元素
  • set() - 更改列表的元素
  • remove() - 从列表中删除一个元素
  • removeAll() - 从列表中删除所有元素
  • clear() - 从列表中删除所有元素(比removeAll()效率更高)
  • size() - 返回列表的长度
  • toArray() - 将列表转换为数组
  • contains() - 如果列表包含指定的元素,则返回true

ArrayList

创建一个ArrayList

List<String> alist = new ArrayList<>(可选大小数字);

由于 ArrayList 实现了 List 接口,所以 alist 变量的类型可以是 List 类型;new 关键字声明后的尖括号中可以不再指定元素的类型,因为编译器可以通过前面尖括号中的类型进行智能推断。

方法

alist.add(int index , E element )添加元素,可填参指定位置

alist.set(int index , E element )更新指定位置的元素

alist.remove(int index)/alist.remove(E element)删除元素,可指定位置或者值

alist.indexOf(Object o)正序查找一个元素,返回坐标值

alist.container(Object o)判断是否包含某个元素

LinkedList链表

Java LinkedList类提供了双向链表实现。

add():一般在链表最后main添加,不过也可以指定位置add(int i,int number)添加

还有特定的contains()方法,判断链表是否包含特定元素

indexOf()&lastIndexOf():返回查找元素第一次出现的位置;

set(i,num)更改位置元素。

堆栈Stack

Stack<T> s = new Stack<>()

Operation:

push()

pop()

peek()

empty()

Queue队列

Queue接口包括Collection接口的所有方法。 这是因为Collection是Queue的超级接口。

Queue接口的一些常用方法是:

  • add() - 将指定的元素插入队列。如果任务成功,则add()返回true,否则将引发异常。
  • offer() - 将指定的元素插入队列。如果任务成功,则offer()返回true,否则返回false。
  • element() - 返回队列的开头。如果队列为空,则引发异常。
  • peek() - 返回队列的开头。 如果队列为空,则返回null。
  • remove() - 返回并删除队列的头部。如果队列为空,则引发异常。
  • poll() - 返回并删除队列的开头。 如果队列为空,则返回null。

优先队列priorityqueue

Queue<T> pq = new PriorityQueue<>()

额外操作Operation:

remove()移除指定元素

contains()判断是否存在

toArray()转化为数组

size()获得大小

Deque

双端队列的方法

由于Deque继承了Queue接口,因此它继承了Queue接口的所有方法。

除了Queue接口中可用的方法之外,Deque界面还包括以下方法:

  • addFirst() - 在双端队列的开头添加指定的元素。如果双端队列已满,则引发异常。
  • addLast() - 在双端队列的末尾添加指定的元素。如果双端队列已满,则引发异常。
  • offerFirst() - 在双端队列的开头添加指定的元素。如果双端队列已满,则返回false。
  • offerLast() - 在双端队列的末尾添加指定的元素。如果双端队列已满,则返回false。
  • getFirst() - 返回双端队列的第一个元素。如果双端队列为空,则引发异常。
  • getLast() - 返回双端队列的最后一个元素。如果双端队列为空,则引发异常。
  • peekFirst() - 返回双端队列的第一个元素。如果双端队列为空,则返回null。
  • peekLast() - 返回双端队列的最后一个元素。如果双端队列为空,则返回null。
  • removeFirst() - 返回并删除双端队列的第一个元素。如果双端队列为空,则引发异常。
  • removeLast() - 返回并删除双端队列的最后一个元素。如果双端队列为空,则引发异常。
  • pollFirst() - 返回并删除双端队列的第一个元素。如果双端队列为空,则返回null。
  • pollLast() - 返回并删除双端队列的最后一个元素。如果双端队列为空,则返回null。

ArrayDeque双向队列

Map

在Java中,Map元素存储在键/值对中。 键是与各个值相关联的唯一值。

Map集合不能包含重复的键。并且,每个键都与一个值相关联。

**注意:**Map接口维护3个不同的集合:

  • 键集
  • 值集
  • 键/值关联(Map集合)的集合。

**注意:**Map接口维护3个不同的集合:

  • 键集
  • 值集
  • 键/值关联(Map集合)的集合。

因此,我们可以分别访问键,值和关联。

由于Map是接口,因此无法从中创建对象。

为了使用Map接口的功能,我们可以使用以下类:

这些类在集合框架中定义并实现Map接口。

方法

Map接口包括Collection接口的所有方法。这是因为Collection是Map的超级接口。

除了Collection接口中可用的方法之外,Map接口还包括以下方法:

  • put(K,V) - 将键K和值V的关联插入到map中。如果键已经存在,则新值将替换旧值。
  • putAll() - 将指定Map集合中的所有条目插入此Map集合中。
  • putIfAbsent(K,V) - 如果键K尚未与value关联,则插入关联V。
  • get(K) - 返回与指定键K关联的值。如果找不到该键,则返回null。
  • getOrDefault(K,defaultValue) - 返回与指定键K关联的值。如果找不到键,则返回defaultValue。
  • containsKey(K) - 检查指定的键K是否在map中。
  • containsValue(V) - 检查指定的值V是否存在于map中。
  • replace(K,V) - 将键K的值替换为新的指定值V。
  • replace(K,oldValue,newValue) - 仅当键K与值oldValue相关联时,才用新值newValue替换键K的值。
  • remove(K) - 从键K表示的Map中删除条目,并返回值
  • remove(K,V) - 如果kv相符,从Map集合中删除键K与值V相关联的条目。
  • keySet() -返回Map集合中存在的所有键的集合。
  • values() -返回一组包含在Map集合中的所有值。
  • entrySet() -返回map中存在的所有键/值映射的集合。

hashMap

访问hashmap元素,使用entrySet(),keySet()和values()

  • entrySet() -返回一组所有键/值映射的map
  • keySet() -返回map所有键的集合
  • values() -返回map所有值的集合

集合Set

与List接口不同,Set集合不能包含重复的元素。

Set的一些额外操作都是针对于数学概念集合上的一些操作,比如求并集,交集:

  • containsAll() - 如果集合包含指定集合的所有元素,则返回true
  • toArray() - 因为类似List的数组形式,所以也可以转化为数组
  • addAll() - 和另一个集合求并集
  • retainAll() - 和另一个集合求交集
  • removeAll()- 和另一个集合求差集,也就是删除包含另一个集合中元素的集合内的所有元素。
  • x.containsAll(y) - 判断x是否全包含y,含义也就是y是否是x的子集

算法Algorithms

Java集合框架提供了各种算法,可用于处理存储在数据结构中的元素。

Java中的算法是静态方法,可用于对集合执行各种操作。

由于算法可用于各种集合,因此也称为通用算法

**sort()**使用sort来进行排序,使用:Collection.sort(T);

**shuffle()**刚好和sort相反,他是用来打乱集合内所有元素的

copy() - 创建从指定源到目标的元素副本

swap() - 交换集合中两个元素的位置

还有Java集合框架的min()和max()方法分别用于查找最小和最大元素。

Iterator接口

Java集合框架的Iterator接口允许我们访问集合的元素。它有一个子接口ListIterator。

Iterator接口提供了四种方法:

  • **hasNext()**如果集合中存在元素,则返回true
  • next()-返回集合的下一个元素
  • remove() -删除next()返回的最后一个元素
  • **forEachRamaining()**对集合的每个剩余元素执行指定的操作

I/O输入输出

在Java中,流是从源读取并写入目标的数据序列。

输入流用于从源读取数据。并且,输出流用于将数据写入目标。

根据流包含的数据,可以将其分类为:

  • 字节流
  • 字符流

字节流(Byte): 它处理单元为1个字节(byte),操作字节和字节数组,存储的是二进制文件,如果是音频文件、图片、歌曲,就用字节流好点(1byte = 8位);字节流的主要类是:InputStream和OutputStream

字符流: 它处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,如果是关系到中文(文本)的,用字符流好点(1Unicode = 2字节 = 16位);主要类是:Reader和Writer;

InputStream类

InputStream类提供了由其子类实现的不同方法。以下是一些常用的方法

  • read() - 从输入流中读取一个字节的数据
  • read(byte[] array) - 从流中读取所有字节并存储在指定的数组中**(和不带参数的相比他一次读取全部字节)**-常用
  • available() - 返回输入流中可用的字节数
  • mark() - 标记输入流中数据所在的位置
  • reset() -将控制点返回到流中设置标记的点
  • markSupported()- 检查流中是否支持mark()和reset()方法
  • skips() - 跳过和丢弃输入流中的指定字节数
  • close() - 关闭输入流

Java Scanner 类

java.util包的Scanner类用于从不同的源(例如输入流,用户,文件等)读取输入数据。让我们举个实例。

import java.util.Scanner;

class Main {
public static void main(String[] args) {

//创建一个对象 Scanner
Scanner input = new Scanner(System.in);

System.out.print("输入姓名: ");

// 从键盘接收输入
String name = input.nextLine();

// 打印输出姓名
System.out.println("我的名字是 " + name);

// Closes the scanner
input.close();
}
}

Java进阶