一、Java概述
1.Java是什么?
Java是一门面向对象的编程语言,作为静态面向对象编程语言的代表,很好地实现了面向对象的各种特性,如封装、继承、多态等,能够以简洁、优雅的方式处理复杂的编程问题。不仅如此,Java还是一个完整的技术体系,涵盖了从开发工具、编译器、虚拟机到应用运行环境的一整套技术支持。Java不仅在传统软件开发领域占据重要地位,还广泛应用于嵌入式系统、移动开发、企业级应用等多个行业,是一门非常优秀的编程语言。
2.Java的发展史
Java的前身是Oak,由詹姆斯·高斯林(James Gosling)于1991年在Sun Microsystems(后来被甲骨文公司收购)开发。起初,Oak是为了开发一种能够运行在多种设备上的程序,特别是家用电子设备和嵌入式系统。然而,随着互联网的兴起,Oak项目被重新定位为一种面向网络的编程语言,并在1995年正式更名为Java。
1995年5月,Java 1.0正式发布,这标志着Java语言及其平台的正式诞生。Java 1.0引入了Java平台的核心特性,包括Java虚拟机(JVM)、Java编程语言、Java API等。Java的设计初衷是“一次编写,到处运行”(Write Once, Run Anywhere, WORA),这一理念极大地推动了Java的普及和应用。
3.Java的特点
-
面向对象:Java是纯粹的面向对象编程语言,几乎所有内容都以对象和类的形式存在。面向对象的特性包括封装、继承、多态,使得代码更易于维护、扩展和复用。
-
跨平台性:Java的跨平台能力源自其在不同系统上运行的Java虚拟机(JVM)。Java代码被编译为字节码(bytecode),然后通过JVM在不同的平台上运行,而无需重新编译代码。
-
安全性:Java设计之初就特别注重安全性。它提供了多层次的安全机制,允许开发人员创建健壮的、抵御潜在攻击的应用程序。尤其是在网络传输、远程操作等应用场景中,Java的安全机制得到了广泛使用。
-
垃圾回收机制:Java具有自动的垃圾回收系统(Garbage Collection, GC),它能够帮助开发者管理内存,减少手动处理内存泄漏和指针操作带来的复杂性。
-
丰富的类库和API:Java提供了丰富的标准类库,包括用于数据结构、I/O操作、网络编程、多线程、GUI等方面的API。这些类库极大地提升了开发效率,简化了许多常见功能的实现。
-
多线程支持:Java内置对多线程的支持,能够轻松处理并发任务。开发者可以使用线程管理并发任务,提高程序的响应速度和处理能力。
-
广泛应用领域:Java不仅被用于桌面应用和Web开发,还广泛应用于移动应用(Android使用Java作为主要开发语言之一)、企业级系统开发、大数据处理(如Hadoop)和物联网(IoT)。
二、Java基本语法
1.注释
-
单行注释
-
多行注释
-
文档注释(Javadoc注释)
2.关键字
3.标识符
-
命名规则
1.标识符可以包含字母、数字、下划线(_)、美元符号($),但不能包含其他符号如@、#等。
2.不能以数字开头。
3.不能是Java的关键字
4.区分大小写
-
命名惯例(见名知意)
1.变量和方法名通常使用小驼峰命名法,即第一个单词小写,后续单词的首字母大写。
2.类和接口名通常使用大驼峰命名法,即每个单词的第一个字母大写
3.常量名通常所有字母大写,并用下划线(_)分隔单词
4.包名通常采用小写字母,如果包名较长,可以用点号(.)分隔
4.变量
-
变量的声明
数据类型 变量名 = 数据值;
数据类型:限定了变量能存储数据的类型 int(整数)double(小数)
变量名:就是存储空间的名字,方便以后使用
=:赋值,把右边的数据赋值给左边的变量
数据值:真正存在变量中的数据
-
基本数据类型
1.整数类型:byte, short, int, long
整数默认为int型,long声明须后加'l'或'L'
2.浮点类型:float, double
浮点数默认为double型,float声明须后加'f'或'F'
3.字符类型:char
2字节
4.布尔类型:boolean
不可用0和非0的整数数代替true和flase
-
基本数据类型转换
- 自动类型转换(隐式转换):容量小的类型自动转换位容量大的数据类型。
- 强制类型转换(显式转换):将容量大的数据类型转换为容量小的数据类型。
使用时要加上强制转换符:(<数据类型>),但可能造成精度降低或溢出, 格外要注意。
5.运算符
-
算术运算符
-
赋值运算符
-
关系运算符
用于比较两个值,返回布尔值(true或false)
-
逻辑运算符
-
位运算符
-
三目运算符
条件表达式 ? 表达式1 : 表达式2
如果条件为true,则返回表达式1的结果;如果条件为false,则返回表达式2的结果。
6.程序流程结构
-
选择结构(分支结构)
if-else结构
switch-case结构
switch-case语句执行时会先进行匹配,匹配成功返回当前case的值,再根据是否有 break,判断是继续输出或是跳出判断。
-
循环结构
for循环
执行顺序:初始化部分→判断循环条件→循环体→迭代更新变量值→判断循环条件→循环体→……→直到不满足循环条件时循环终止。
while循环
执行顺序与for循环一样
while循环与for循环的区别:
1.for循环适用于已知循环次数或需要通过计数器控制循环的情况。
2.while循环适用于当循环次数不确定,或者条件变化较为复杂的情况。
do-while循环
执行顺序:初始化部分→循环体→迭代更新变量值→判断循环条件→循环体→……→直到不满足循环条件时循环终止。
do-while循环与while循环的区别:
1.while循环在每次循环开始之前检查条件表达式。
2.do-while循环在每次循环开始之前先执行一次循环体,然后检查条件表达式。
-
跳转结构
break:跳出当前循环或switch语句。
continue:跳过当前循环的剩余部分,直接进入下一次循环。
return:退出当前方法,返回到调用者。
三、基础类库
1.字符串
-
创建
-
常用方法
-
StringBilder和StringBuffer
StringBuilder和StringBuffer是可变字符串类,即它们可以在现有的字符串基础上进行修改,而不创建新的对象。
常用方法
区别:
2.数组
-
一维数组
声明: 数据类型[ ] 数组名;
初始化
静态初始化
动态初始化
访问:通过索引访问数组中的元素,索引从0开始。
长度:数组的长度通过.length属性获取。
遍历:可以使用for循环或增强型for循环来遍历数组。
-
多维数组
声明: 数据类型[ ][ ] 数组名;
初始化
静态初始化
动态初始化
-
数组使用异常
数组越界:访问数组时,如果索引超出数组的范围,会抛出 ArrayIndexOutOfBoundsException异常。
空指针异常 :访问或者操作一个一个为null的数组时,会抛出NullPointerException异常。
3.集合
-
概述
集合(Collections)是用于存储和操作一组数据的框架。Java 提供了多种集合类来存储和操作对象,它们位于java.util包中。集合框架提供了一套标准的接口和类,可以轻松操作数据,并解决常见的编程需求。
-
Collection接口
Collection是所有集合类的根接口,派生了以下子接口:
List是一种有序的集合,允许包含重复的元素。
Set是一种无序的集合,不允许有重复元素。
Queue是一种遵循先进先出(FIFO)原则的集合
-
Map接口
Map
是用于存储键值对的数据结构。
-
Iterator 接口
Iterator
提供遍历集合的方式,Iterator
适用于 Collection
的所有子类
-
遍历
1. 使用 Iterator
遍历
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
2. 使用增强型 for
循环遍历
for (String s : list) {
System.out.println(s);
}
-
集合和数组的区别
使用数组:当知道数据的大小是固定的,且需要频繁通过索引进行访问时
使用集合:当需要灵活地增加、删除或操作数据,或者处理不同类型的复杂数据结构时
四、面向对象
1.类和对象
-
类
类是对一类事物的描述,是抽象的,概念上的定义。
基本组成部分:
- 属性:类中定义的变量,描述对象的特征。
- 行为:类中的方法,定义对象的功能和行为。
- 构造方法:用于初始化对象的特殊方法。
- 静态成员:使用
static
修饰的变量或方法,属于类本身,而不属于某个对象。
定义
class ClassName {
// 属性
String name;
int age;
// 构造方法
ClassName(String name, int age) {
this.name = name;
this.age = age;
}
// 方法(行为)
public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
}
-
对象
对象是根据类创建的实例,是实际存在的事物个体。
创建对象:
- 定义类:首先要有一个类作为对象的模板。
- 实例化对象:使用new关键字调用类的构造方法来创建对象。
- 访问对象的属性和方法:通过对象可以访问类中的属性和方法。
public class Main {
public static void main(String[] args) {
// 创建对象
ClassName person = new ClassName("Alice", 25); // 使用类 ClassName 实例化对象
// 访问对象的属性和方法
person.displayInfo(); // 调用对象的方法
}
}
-
类和对象的关系
- 类是模板,对象是类的实例。
- 类定义了对象的属性和行为,对象通过实例化类来实现这些属性和行为。
- 多个对象可以通过同一个类创建,每个对象有自己独立的属性,但它们共享相同的行为(方法)。
2.方法
-
定义
修饰符(可选):public、private、static等。
返回类型:方法返回的值的类型。如果方法不返回值,使用void。
方法名:方法的名称,遵循Java 的标识符命名规则。
参数列表:方法接受的参数,可以为空。如果有多个参数,用逗号分隔。
方法体:包含方法的具体代码。
返回值:通过 return
语句返回方法的计算结果,如果返回类型为 void
,则可以省略 return
。
-
类型
1. 无返回值的无参方法
public void greet() {
System.out.println("Hello, World!");
}
2. 有参无返回值的方法
public void printMessage(String message) {
System.out.println(message);
}
3. 有参有返回值的方法
public int add(int a, int b) {
return a + b;
}
4. 静态方法:使用static关键字修饰的方法
public static int add(int a, int b) {
return a + b;
}
-
调用
在同一个类中调用:在同一个类中,方法之间的调用是非常简单的。既可以直接调用静态方法,也可以直接调用非静态方法。
public class Example {
// 定义一个静态方法
public static void method1() {
System.out.println("This is method1");
}
// 定义一个非静态方法
public void method2() {
System.out.println("This is method2");
method3(); // 非静态方法可以直接调用非静态方法
}
// 另一个非静态方法
public void method3() {
System.out.println("This is method3");
method1(); // 非静态方法可以直接调用静态方法
}
public static void main(String[] args) {
method1(); // 静态方法可以直接调用静态方法
// method2(); 静态方法不能直接调用非静态方法
Example example = new Example();
example.method2(); // 通过对象调用非静态方法
}
}
为什么静态方法不能直接调用非静态方法?
- 静态方法属于类本身,在没有对象的情况下也可以通过类名调用。
- 非静态方法属于对象,它需要依赖具体的对象实例才能调用。
- 静态方法不包含对象的上下文(即没有
this
引用),因此不能访问或操作与对象相关的非静态成员。
从另一个类调用:
调用静态方法:静态方法属于类本身,而不是对象。调用静态方法时,不需要创建对象,只需通过类名调用。
public class ClassA {
// 静态方法
public static void staticMethod() {
System.out.println("ClassA");
}
}
public class ClassB {
public static void main(String[] args) {
ClassA.staticMethod(); // 直接通过类名调用 ClassA 中的静态方法
}
}
调用非静态方法:非静态方法属于对象,因此在不同类中调用非静态方法时,需要先创建该类的对象。
public class ClassA {
// 非静态方法
public void displayMessage() {
System.out.println("ClassA");
}
}
public class ClassB {
public static void main(String[] args) {
ClassA classA = new ClassA(); // 创建 ClassA 的对象
classA.displayMessage(); // 调用 ClassA 中的非静态方法
}
}
-
方法重载
在同一类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
public class OverloadingExample {
// 方法重载
public void print(int num) {
System.out.println("Number: " + num);
}
public void print(String message) {
System.out.println("Message: " + message);
}
public void print(int num, String message) {
System.out.println("Number: " + num + ", Message: " + message);
}
public static void main(String[] args) {
OverloadingExample example = new OverloadingExample();
example.print(5); // 输出: Number: 5
example.print("Hello"); // 输出: Message: Hello
example.print(10, "Java"); // 输出: Number: 10, Message: Java
}
}
-
参数传递
对于基本数据类型:采用值传递机制。如果参数是基本数据类型,传递的是真实的数据,形参的改变,不影响实参的值。
对于引用数据类型:如果参数是引用数据类型,传递的是地址值,形参的改变,影响实参的值
3.构造方法
构造方法(Constructor) 是一种特殊的方法,用于在创建对象时初始化对象的状态。
-
特点
1.没有返回类型:构造方法没有返回类型,甚至不能写 void。
2.与类名相同:构造方法的名字必须和类名完全相同。
3.自动调用:构造方法在使用new关键字创建对象时自动调用。
4.可以重载:一个类可以有多个构造方法,称为构造方法的重载,它们通过不同的参数列表来区
-
类型
1. 无参构造方法(默认构造方法)
如果一个类没有定义任何构造方法,Java 会自动提供一个无参构造方法,也称为默认构造方法。如果定义了其他构造方法,那么 Java 将不再提供默认的无参构造方法,开发者需要自己定义。
public class Person {
String name;
int age;
// 无参构造方法
public Person() {
this.name = "Madalie";
this.age = 18;
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person(); // 调用无参构造方法
System.out.println(person.name); // 输出: Madalie
System.out.println(person.age); // 输出: 18
}
}
2. 有参构造方法
有参构造方法允许在创建对象时直接给对象的属性赋值,通过传递参数来初始化对象的状态。可以根据需要定义多个有参构造方法。
public class Person {
String name;
int age;
// 有参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("Mdalie", 18); // 调用有参构造方法
System.out.println(person.name); // 输出: Madalie
System.out.println(person.age); // 输出: 18
}
}
-
重载
在一个类中可以定义多个构造方法,参数列表不同,称为构造方法重载。根据传入参数的不同,Java 会自动选择合适的构造方法。
public class Person {
String name;
int age;
// 无参构造方法
public Person() {
this.name = "Madalie";
this.age = 18;
}
// 有参构造方法
public Person(String name) {
this.name = name;
this.age = 22;
}
// 有参构造方法重载
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person(); // 调用无参构造方法
Person p2 = new Person("Bob"); // 调用单参数构造方法
Person p3 = new Person("Charlie", 25); // 调用双参数构造方法
System.out.println(p1.name + ", " + p1.age); // 输出: Madalie, 18
System.out.println(p2.name + ", " + p2.age); // 输出: Bob, 22
System.out.println(p3.name + ", " + p3.age); // 输出: Charlie, 25
}
}
4.封装
-
权限修饰符
-
特点
- 私有化属性:类的属性通常设置为private,即该属性只能在类的内部被访问,外部类不能直接修改或读取。
- 公开访问方法:通过public修饰的getter和setter方法提供了对私有属性的间接访问,外部类通过这些方法来操作对象的状态。
- 数据校验:在setter方法中,可以添加逻辑来检查赋值是否符合预期,从而提高数据的安全性。
-
作用
- 信息隐藏:通过将类的属性设为私有,外部无法直接访问,只有通过类提供的公共方法才能操作这些属性。
- 提高代码的可维护性:外界通过特定的方法访问对象的属性,方法中可以控制逻辑和验证,从而保护数据的安全。
- 灵活性和可扩展性:类可以改变内部实现而不影响外部代码,只需保持公共接口不变即可。
- 控制权限:封装可以通过访问修饰符(private、protected、public)控制类成员的访问权限。
实现步骤
- 将类的属性声明为私有(private),防止外部类直接访问。
- 提供公共的getter和setter方法,用于对私有属性进行读写操作。
- 在getter和setter中可以添加数据校验逻辑,从而控制属性的合法性。
public class User {
// 私有属性
private String name;
private int age;
// 构造方法
public User(String name, int age) {
this.name = name;
setAge(age); // 调用 setter 方法进行验证
}
// 公共的 getter 方法,用于获取 name
public String getName() {
return name;
}
// 公共的 setter 方法,用于设置 name
public void setName(String name) {
this.name = name;
}
// 公共的 getter 方法,用于获取 age
public int getAge() {
return age;
}
// 公共的 setter 方法,用于设置 age
public void setAge(int age) {
if (age > 0 && age < 100) {// 在 setter 中添加数据校验逻辑
this.age = age;
} else {
System.out.println("数据无效,请重新输入!");
}
}
}
5.继承
允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。
-
基本概念
- 父类(基类 / 超类,Superclass):被继承的类,父类包含子类可以复用的属性和方法。
- 子类(派生类,Subclass):继承父类的类,子类不仅继承了父类的成员,还可以添加新的成员或重写父类的方法。
-
特点
- 单继承:在 Java 中,一个类只能有一个直接的父类,不能同时继承多个类。Java 通过接口(interface)来实现多重继承的功能。
- super 关键字:用于调用父类的构造方法或访问父类的方法和属性。子类可以使用super 关键字访问父类的成员。
- 子类拥有父类的属性和方法:子类可以直接使用父类的公共和受保护成员。
- 子类可以扩展或重写父类:子类可以添加新的方法和属性,也可以重写父类的方法,实现多态性。
-
作用
- 代码复用:子类继承了父类的代码,避免重复编写相同的逻辑。
- 提高代码可维护性:修改父类的代码,所有子类都会受益,统一管理和更新。
- 提高扩展性:子类可以在父类的基础上进行扩展或重写功能,适应不同的需求。
-
实现
- 定义父类,包含基本属性和方法。
- 定义子类,使用extends关键字。
- 创建子类对象,可以访问父类方法。
- 重写父类方法,如需要自定义行为。
- 使用 super关键字,调用父类方法或构造器。
class Animal {
// 父类的属性
String name;
// 父类的构造方法
public Animal(String name) {
this.name = name;
}
// 父类的方法
public void makeSound() {
System.out.println(name + "在叫");
}
}
class Dog extends Animal {
// 子类的构造方法
public Dog(String name) {
super(name); // 使用 super 调用父类构造方法
}
// 子类重写父类的方法
@Override
public void makeSound() {
System.out.println(name + "汪汪汪");
}
}
public class Main {
public static void main(String[] args) {
// 创建父类对象
Animal animal = new Animal("动物");
animal.makeSound(); // 调用父类方法
// 创建子类对象
Dog dog = new Dog("百万");
dog.makeSound(); // 调用重写后的子类方法
}
}
-
方法重写
方法重写是指子类提供了对父类方法的新的实现,用于替换父类的方法。重写时,方法名、参数列表和返回类型必须与父类的方法一致。
@Override注解:Java 提供了 @Override注解来标识方法重写,这不仅能提高代码的可读性,还能让编译器帮助检查是否真正覆盖了父类的方法。
class Animal {
public void makeSound() {
System.out.println("动物叫");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}
-
Object类
Object类是所有类的父类。也就是说,每个类默认都继承自 Object类(即使没有显式声明)。
常用方法
6.多态
多态是指同一操作可以作用于不同类型的对象上,主要通过方法重载和方法重写实现。
类型
1. 方法重载(编译时多态)
方法重载是指同一个类中多个方法具有相同的名称,但参数类型或数量不同。编译器根据参数的类型和数量来决定调用哪个方法。
class MathUtil {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
}
2. 方法重写(运行时多态)
方法重写是指子类重新定义父类的方法。运行时多态允许通过父类引用指向子类对象,从而调用子类重写的方法。
class Animal {
void sound() {
System.out.println("动物叫");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("喵喵喵");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog(); // 父类引用指向子类对象
Animal myCat = new Cat();
myDog.sound(); // 输出: 汪汪汪
myCat.sound(); // 输出: 喵喵喵
}
}
-
注意
- 多态只能通过继承或接口实现。
- 父类引用指向子类对象时,调用的方法是根据实际对象的类型来决定的,而不是根据引用的类型。
7.抽象类
抽象类是 Java 中的一种特殊类,用于定义一组方法的模板,但不提供这些方法的具体实现。抽象类通常用于为子类提供基本的框架,子类可以继承抽象类并实现具体的方法。
-
定义
使用abstract关键字定义抽象类。
abstract class Animal {
// 抽象方法
public abstract void makeSound();
// 非抽象方法
public void eat() {
System.out.println("吃东西");
}
}
-
实现
子类继承抽象类并实现其抽象方法
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}
-
调用
通过子类实例化并调用方法
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.makeSound(); // 输出 "汪汪汪"
myDog.eat(); // 输出 "吃东西"
Animal myCat = new Cat();
myCat.makeSound(); // 输出 "喵喵喵"
myCat.eat(); // 输出 "吃东西"
}
}
特点
- 抽象类不能被实例化,必须通过子类来实例化。
- 抽象类可以有构造方法,供子类调用。
- 抽象类可以包含实例变量和方法(抽象方法和非抽象方法)。
- 子类必须实现抽象方法,如果子类不实现所有抽象方法,子类也必须声明为抽象类。
-
抽象方法
抽象方法是在抽象类中使用abstract关键字定义,只有方法的声明,没有方法体的方法。
-
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法;
8.接口
接口(Interface)是一种特殊的引用类型,用于定义类的行为规范。接口只包含方法的声明,而不包含方法的实现。
-
定义
使用interface关键字来定义
interface Animal {
void makeSound(); // 抽象方法
default void eat() { // 默认方法
System.out.println("吃东西");
}
}
实现
实现一个接口,类必须使用implements关键字,并提供接口中所有方法的实现。
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}
-
调用
一旦类实现了接口,就可以创建对象并调用接口中定义的方法。
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog(); // 使用接口类型引用
myDog.makeSound(); // 输出 "汪汪汪"
myDog.eat(); // 输出 "吃东西"
Animal myCat = new Cat();
myCat.makeSound(); // 输出 "喵喵喵"
myCat.eat(); // 输出 "吃东西"
}
}
-
特点
-
方法声明:接口只能包含方法的声明(默认是public abstract),不包含具体实现。Java 8 及以后,接口可以包含默认方法(使用default关键字)和静态方法。
-
常量:接口中定义的变量都是public static final 类型,即常量。
-
多重继承:一个类可以实现多个接口,解决了 Java 的单继承限制。
-
实现:实现接口的类必须实现接口中的所有方法,除非该类是抽象类。
-
不能包含构造方法:接口不能有构造方法,因此不能实例化接口。
-
抽象类和接口的区别