目的:对javase基础知识点进行快速回顾
Java语言特性
-
平台无关性:Java代码编译成平台无关的字节码(Bytecode),可以在任何支持Java虚拟机(JVM)的平台上运行。
-
面向对象编程:Java支持类、接口等面向对象概念,如封装、继承、多态等。
-
简单性:Java的语法相对简单,易于学习和使用。
-
分布式处理:Java提供了强大的网络通信和远程方法调用(RMI)能力,适合分布式系统的开发。
-
健壮性:Java提供了垃圾回收机制,防止了大部分内存泄漏问题,同时提供了异常处理机制。
-
安全性:Java提供了一个安全模型,提供了字节码的认证和授权机制,确保代码的完整性。
-
多线程处理:Java提供了多线程编程能力,简化了多线程程序的开发和管理。
-
动态性:Java提供了反射(Reflection)API,可以在运行时动态加载和使用类库,提高了动态性
数据类型:
1个字=2个字节=16位 1word = 2Byte = 16bit
类型转换
小范围转大范围(自动转换)
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
大范围转小范围(强制转换)
注意:单精度,双精度转换整形小数会丢失
运算符
算术运算符:+ - * / %(取余)++ --
赋值运算符:= += -= *= /+ %=
比较运算符:== != < > <= >=
逻辑运算符:&与 |或 ^异或 !非 &&短路与 ||短路或
注:&与&&的区别 &:左侧无论是否为假右侧都会进行运算 &&:左侧为假右侧不再进行运算 |与||的区别类似:||:左侧为真右侧不再进行运算 而 | 需要运算右侧
条件语句
if...else if...else switch(表达式){case 目标值:执行语句 break;default}
表达式:byte,short,int,char,String,枚举
三元运算符:判断条件 ?表达式1:表达式2; 成立表达式1作为结果,不成立表达式2作为结果
循环语句
while do...while for(int i;i<=x;i++)
break语句 结束循环(可以添加标签指定跳出那个循环) ; continue语句 结束本次循环进入下一次循环
重载与重写
1. 重载(Overload)
- 定义:在同一个类中,允许多个同名方法,只要它们的参数列表不同即可。参数列表的不同包括参数的数量不同、参数的类型不同或参数的顺序不同。
- 目的:提供了一个接口的多种实现方式,以便可以根据不同的参数执行不同的操作。
- 注意点:
- 方法名必须相同。
- 参数列表必须不同(个数、类型或顺序)。
- 返回值类型可以相同也可以不同。
- 访问修饰符的限制一定要大于被重写方法的访问修饰符(对于子类重写父类方法时,这条不适用,但在同一个类中定义重载方法时需要注意访问权限的问题)。
- 可以发生在一个类中,是编译时多态的一种体现。
2. 重写(Override)
- 定义:子类有一个方法,其名称、返回类型及参数列表与父类中的某个方法完全相同。
- 目的:允许子类提供特定于自身的实现。
- 注意点:
- 方法名、参数列表、返回类型必须完全相同。
- 访问权限不能比父类中被重写的方法的访问权限更低(子类中的重写方法可以扩大访问权限)。
- 子类方法抛出的异常应该比父类方法抛出的异常更具体或完全不抛出异常。
- 是实现多态性的一种重要手段,允许父类引用指向子类对象。
- 需要使用
@Override
注解来明确表示该方法为重写方法(这不是必需的,但推荐这样做以提高代码的可读性)。
数组
声明数组
类型[] 数组名; - 先声明一个数组变量,之后再用new关键字来分配内存空间。
类型[] 数组名 = new 类型[大小]; - 声明数组的同时分配内存空间。
类型[] 数组名 = {值1, 值2, ..., 值n}; - 在声明数组的同时初始化数组。
一维数组
二维数组
类与对象
类(Class)
类是创建对象的模板或蓝图,它定义了对象的属性(即数据)和方法(即行为)。一个类可以包含以下类型变量:
- 局部变量:在方法、构造器或者代码块中定义的变量。
- 成员变量:在类体中定义的变量,属于类的成员。
- 类变量:用
static
关键字声明的变量,属于类本身,而不是类的某个对象。
类还可以包含方法,方法定义了对象可以执行的操作。方法可以有参数,这些参数在方法被调用时接收数据。
对象(Object)
对象是类的实例。当声明一个类的变量时,你实际上是在为那个类的对象引用分配了存储空间。要创建类的实例(即对象),需要使用new
关键字后跟类名(以及构造器,如果需要的话)
面向对象的三大特征
1.封装
封装是将对象的属性(数据)和操作这些属性的方法(函数)结合在一起,形成一个独立的单元(即类)。这样,对象的内部状态(即属性)就被隐藏在类的内部,而外部只能通过类提供的方法来访问这些内部状态。封装的目的在于提高代码的安全性和可维护性。
隐藏内部实现细节,对外提供公共的访问接口(方法)。
实现:使用private
关键字修饰类的属性,使得这些属性只能在类内部被访问,提供public
的getter
和setter
方法来访问和修改这些属性。
2.继承(extends)
继承是面向对象编程中的一个重要特性,它允许我们定义一个类(子类)来继承另一个类(父类)的属性和方法。继承可以让我们重用现有的代码,减少代码的冗余。
子类可以继承父类的所有属性和方法,但是只能直接使用非私有的,私有的不行(反射)。子类可以添加新的属性和方法,也可以重写(Override)继承自父类的方法。
关键词:this super
this和super 都能用于访问:成员变量和方法还有构造器 上。
其中,this关键字能够用来帮助我们访问本类的:成员变量和方法还有构造器。
this关键字在Java中代指引用当前Class类对应的对象实例。
当遇到变量名冲突的时候,用来区分成员变量和局部变量。
而super关键字则是专门用于子类访问父类的地方。
它能够用来帮助我们访问父类的:成员变量和方法还有构造器,确保了在子类中可以正确地使用和继承父类的功能。
当遇到子类与父类有重名的成员变量和方法时,super就可以帮助我们明确出:哪些是调用父类的成员变量和方法。
3.多态
1.编译时多态(静态多态)
编译时多态主要通过方法重载(Overloading)实现。方法重载是指同一个类中可以有多个同名方法,只要它们的参数列表不同即可。编译器在编译时会根据方法的参数列表和调用时提供的参数类型来确定使用哪个方法。
例如:
2.运行时多态(动态多态)
运行时多态主要通过方法重写(Overriding)和向上转型(Upcasting)实现。方法重写是指子类可以重写继承自父类的方法,这样当子类对象被当作父类对象使用时,如果调用的是被子类重写的方法,那么就会执行子类的方法。
两种实现方式:继承与方法重写,接口
例如:
在上面的例子中,b
是Animal
类型的引用,但它指向了一个Dog
对象。当调用b.eat()
时,由于Dog
类重写了eat()
方法,所以实际执行的是Dog
类的eat()
方法,这就是多态性的体现。
3.多态注意事项
1. 向上转型和向下转型
- 向上转型是将子类对象转换为父类或接口类型,这是安全的,因为子类总是具有父类或接口的所有方法和属性。
- 向下转型是将父类或接口类型转换为子类类型,这是不安全的,需要进行显式的类型转换,并且在转换之前需要进行类型检查,以避免出现
ClassCastException
异常。
instanceof 判断一个对象是否是某个类的实例
2.方法重写的规则
- 子类重写父类的方法时,方法的签名(方法名、参数列表和返回类型)必须与父类中的方法完全相同。
- 子类重写的方法的访问权限不能低于父类中的方法的访问权限。
- 子类重写的方法不能抛出比父类中的方法更多的异常。
接口与抽象类
1.抽象类
Java中的抽象类是一种特殊的类,它不能被实例化,即不能创建该类的具体对象。抽象类主要用于提供一个或多个抽象方法的框架,这些抽象方法必须由子类具体实现。此外,抽象类还可以包含非抽象的方法和变量,以及构造方法(尽管抽象类不能被直接实例化,但子类在实例化时需要调用父类的构造方法)。
抽象类特点:
-
不能被实例化:这是抽象类最显著的特点,即不能使用
new
关键字来创建抽象类的实例。 -
可以包含抽象方法:抽象方法是没有方法体的方法,它使用
abstract
关键字声明,且必须以分号(;
)结束。抽象方法的具体实现必须由其子类提供。 -
可以有非抽象方法:抽象类不仅可以包含抽象方法,还可以包含非抽象(具体)的方法。这些方法在抽象类中就有实现,子类可以直接继承这些方法,也可以重写它们。
-
可以有变量:抽象类可以包含变量,这些变量可以是实例变量或静态变量。
-
可以有构造方法:虽然抽象类不能被实例化,但它可以有构造方法。这些构造方法主要用于被子类调用,在子类的构造方法中通过
super()
来调用父类的构造方法,进行初始化操作。 -
可以被继承:其他类可以继承抽象类,继承抽象类的子类必须实现父类中的所有抽象方法(除非子类也是抽象类)。
由于抽象类不能被实例化,因此它主要用于作为其他类的基类(父类)。子类可以通过继承抽象类来扩展其功能,并实现抽象类中声明的所有抽象方法。如果子类没有实现所有的抽象方法,则子类也必须被声明为抽象类。
2.接口类
Java接口(Interface)是Java编程语言中的一种引用类型,是一种抽象的类型,用于指定一组方法规范,但不提供这些方法的具体实现。接口是一种形式上的契约,它规定了实现接口的类必须遵循的规则。接口在Java中扮演着非常重要的角色,特别是在实现多态性和解耦方面。
接口特点:
-
抽象性:接口中的所有方法都是抽象的,即它们都没有方法体(即大括号
{}
内没有代码)。Java 8之后,接口中可以包含默认方法(default methods)和静态方法(static methods),这些方法可以有实现体。 -
实现:一个类通过关键字
implements
来实现一个或多个接口。实现接口的类必须提供接口中所有方法的具体实现(除非它们是默认方法或静态方法)。 -
多继承:Java不支持类的多继承,但接口支持多继承。一个接口可以继承一个或多个其他接口。
-
类型安全:接口是定义了一组API的规范,使用接口可以使得代码更加类型安全。
-
解耦:接口有助于实现高内聚低耦合的设计原则,使得系统更加灵活和可扩展。
接口与抽象类的区别:
1.定义关键字:接口用 interface 定义,而抽象类用 abstract class 定义。
2. 继承或实现:类实现接口用 implements,而类继承抽象类用extends。
3.子类数量:一个类可以实现多个接口,但只能继承一个抽象类(除了Java 8的接口可以多继承外)。
4.方法:接口中的方法都是抽象的,不能有具体实现;而抽象类可以有抽象方法和非抽象方法。
5.属性:接口中的属性默认是public static final的,而抽象类中的属性可以是各种类型。
6.实例化:接口不能被实例化,抽象类也不能被直接实例化,但可以通过子类实例化。
多线程
实现方式:
1.继承Thread类
最基本的创建线程的方式。创建一个继承自java.lang.Thread
类的子类,并重写其run()
方法。然后创建该子类的实例来创建新的线程。通过调用线程的start()
方法来启动线程,这会导致JVM调用该线程的run()
方法。
- 简单直观,直接继承
Thread
类即可创建线程。 - 但 Java 不支持多继承,如果一个类已经继承了其他类,就不能再使用这种方式创建线程。
2.实现Runnable接口
实现Runnable
接口的run()
方法。然后创建Thread
类的实例,将Runnable
实现类的实例作为构造器的参数传递给它。这样,当线程启动时,它会调用Runnable
实现类的run()
方法。
- 更加灵活,因为一个类可以实现多个接口,不会受到 Java 单继承的限制。
- 可以将线程的任务和线程的管理分离,使得代码结构更加清晰。
3.实现Callable接口配合FutureTask
创建Thread类的对象重写call方法(有返回值),创建MyCallable的对象(执行的任务),创建FutureTask的对象将任务作为参数传入,创建Thread类的对象将FutureTask的对象作为参数传入,创建线程成功,start()开启线程;
- 可以有返回值,通过
Future
对象可以获取线程执行的结果,这在需要线程执行任务并返回结果的场景非常有用。 - 可以抛出受检异常,方便处理线程执行过程中可能出现的异常情况
三种实现方式的区别
- 如果只需要简单地创建一个线程执行一些任务,且不考虑代码的灵活性和可扩展性,可以使用继承
Thread
类的方式。 - 如果希望代码更加灵活,将线程的任务和线程的管理分离,或者一个类已经继承了其他类,那么实现
Runnable
接口是一个更好的选择。 - 如果需要线程执行任务并返回结果,或者需要处理线程执行过程中的异常情况,那么实现
Callable
接口并结合线程池使用是最合适的方式。
4.用线程池的方式实现多线程,Lambda 表达式(略)
线程的生命周期
大概况:新建,可运行(就绪,运行),阻塞,等待,终止
1.新建状态(NEW):
当一个线程对象被创建,但还未调用其start()
方法时,该线程处于新建状态。此时,线程尚未与操作系统的底层线程关联。
2.就绪状态(RUNNABLE):
当线程调用了start()
方法后,就会由新建状态进入就绪状态。在这个状态下,线程已经与操作系统的底层线程关联,并等待操作系统的调度来执行。此时,线程具备了执行所需的所有资源,除了CPU时间片之外。
3.运行状态(RUNNING):
当就绪状态的线程被操作系统的调度器选中,分配到了CPU时间片时,线程就进入了运行状态。此时,线程正在执行其程序代码.
4.阻塞状态(BLOCKED):
-
线程因为某种原因暂时放弃CPU使用权,并停止运行,就进入了阻塞状态。这种状态可以细分为多种情况,如:
- 其他阻塞:线程执行了
sleep()
、join()
方法,或者发起了I/O请求等,也会进入阻塞状态。 - 同步阻塞:线程在尝试获取某个对象的同步锁时,如果该锁已被其他线程占用,则当前线程会进入阻塞状态,直到锁被释放。
- 等待阻塞:线程执行了
wait()
方法,释放了占用的所有资源,并被放入等待池中。这种状态不能自动唤醒,必须等待其他线程调用notify()
或notifyAll()
方法。
- 其他阻塞:线程执行了
5.等待状态(WAITING):
线程在等待某个条件成立或某个事件发生时,会进入等待状态。这种状态下,线程不会占用CPU资源,并且需要被其他线程显式唤醒,如通过notify()
或notifyAll()
方法。
6.超时等待状态(TIMED_WAITING):
线程在等待某个条件成立或某个事件发生时,还可以指定一个超时时间。如果在这个时间内条件没有成立或事件没有发生,线程会自动从等待状态恢复到就绪状态。这种状态下的线程同样需要被其他线程显式唤醒或等待超时
7.终止状态(TERMINATED):
当线程执行完毕或因为异常而退出run()
方法时,线程会进入终止状态。此时,线程的生命周期结束,其占用的资源也会被释放。
线程池
线程池实现的方式:
1.