Java是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。面向对象编程的关键概念包括对象、类、封装、继承和多态。
-
对象(Object):对象是面向对象编程中的基本单位,它是现实世界中实体的抽象表示。对象包含属性(数据)和行为(方法)。
-
类(Class):类是对象的模板,它定义了对象共有的属性和行为。通过类可以创建多个对象。
-
封装(Encapsulation):封装是指将对象的属性和行为包装起来,使外部无法直接访问,只能通过对象提供的方法进行访问。
-
继承(Inheritance):继承是指一个类从另一个类继承属性和行为,从而减少代码冗余,提高代码的可重用性。
-
多态(Polymorphism):多态是指同一种行为在不同对象上表现出不同的效果,它提高了代码的灵活性。
面向对象编程的优点包括:提高代码的可重用性、可维护性和可扩展性,使代码更加易于理解和维护。
在面向对象编程中,我们还需要关注以下几个关键概念:
-
抽象(Abstraction):抽象是指从具体问题中提取共性,形成更通用的概念。在面向对象编程中,抽象有助于我们更好地设计和组织代码。
-
接口(Interface):接口是一个抽象的概念,它定义了对象应该具备的行为。通过接口,我们可以实现更灵活的代码设计和更好的可扩展性。
-
组合(Composition):组合是指将多个对象组合成一个新的对象,以实现更复杂的功能。组合有助于我们构建更灵活的软件系统。
-
设计模式(Design Patterns):设计模式是针对常见设计问题的解决方案,它们提供了一种有效的设计思路和实践方法。使用设计模式可以提高代码的可重用性和可维护性。
面向对象编程在实际开发中具有广泛的应用,如Java、C++、Python等编程语言都支持面向对象编程。通过运用这些概念和原则,我们可以开发出更高质量的软件系统。
面向对象(Object-Oriented Programming, OOP)和面向过程(Procedural Programming)是两种不同的编程范式。它们的主要区别在于解决问题的思维方式和代码组织方式。
- 思维方式:
-
面向过程:分析出解决问题所需的步骤,然后用函数把这些步骤实现,并按顺序调用。
-
面向对象:将问题分解成一系列与对象交互的步骤。对象包含了数据和方法,数据表示对象的状态,方法表示对象的行为。
- 代码组织方式:
-
面向过程:代码按照功能性进行划分,数据和函数分离。
-
面向对象:代码按照对象进行划分,数据和方法封装在对象中。
- 适用场景:
-
面向过程:适用于较小的项目,需求变化不大的场景。
-
面向对象:适用于较大的项目,需求变化频繁的场景。
- 示例:
-
面向过程:假设我们要实现一个简单的计算器,我们可能会定义一些函数,如add()、subtract()、multiply()等,然后根据需要调用这些函数。
-
面向对象:在面向对象的方式中,我们可能会定义一个名为Calculator的类,这个类包含了一些方法,如add()、subtract()、multiply()等,同时包含了一些属性,如操作的数字等。我们可以创建Calculator类的对象,然后调用它的方法。
总的来说,面向对象和面向过程各有优缺点,选择哪种方式取决于项目的具体需求和开发人员的偏好。
Java类与对象是Java编程中基础的概念,以下内容将帮助你理解并掌握这一概念:
-
Java类和对象:
-
类:类是具有相同属性和相同行为的对象的集合。类定义了对象的属性和行为。
-
对象:对象是类的实例,具有类定义的属性和行为。
-
-
创建类:
-
使用
class
关键字创建类。 -
每个类都有一个构造函数,用于初始化对象的属性。
-
构造函数的名称与类名相同,没有返回类型。
// 创建类 class ClassName{ field; // 字段(属性) 或者 成员变量 method; // 行为 或者 成员方法 } //class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类具有哪些功能,称为类的成员方法。
class WashMachine{ public String brand; // 品牌 public String type; // 型号 public double weight; // 重量 public double length; // 长 public double width; // 宽 public double height; // 高 public String color; // 颜色 public void washClothes(){ // 洗衣服 System.out.println("洗衣功能"); } public void dryClothes(){ // 脱水 System.out.println("脱水功能"); } public void setTime(){ // 定时 System.out.println("定时功能"); } }
注意事项
-
类名注意采用大驼峰定义
-
成员前写法统一为public,后面会详细解释
-
此处写的方法不带 static 关键字. 后面会详细解释
-
-
创建对象/类的实例化:
-
使用
new
关键字创建对象。 -
构造函数用于初始化新创建的对象。
class PetDog { public String name;//名字 public String color;//颜色 // 狗的属性 public void barks() { System.out.println(name + ": 旺旺旺~~~"); } // 狗的行为 public void wag() { System.out.println(name + ": 摇尾巴~~~"); } } public class Main{ public static void main(String[] args) { PetDog dogh = new PetDog(); //通过new实例化对象 dogh.name = "阿黄"; dogh.color = "黑黄"; dogh.barks(); dogh.wag(); PetDog dogs = new PetDog(); dogs.name = "赛虎"; dogs.color = "棕色"; dogs.barks(); dogs.wag(); } } 输出结果: 阿黄: 旺旺旺~~~ 阿黄: 摇尾巴~~~ 赛虎: 旺旺旺~~~ 赛虎: 摇尾巴~~~
-
-
this引用:
在编程中,“this”通常是一个关键字,用于引用当前对象。在许多面向对象的编程语言中,比如Java、C++、JavaScript等,“this”通常指代当前正在执行代码的对象实例。使用“this”关键字可以访问当前对象的属性和方法。在Java中,“this”关键字用于引用当前对象的实例。它可以在实例方法中使用,用来指代当前正在调用方法的对象。
public class Person { private String name; public Person(String name) { this.name = name; } public void printName() { System.out.println("My name is " + this.name); } }
为什么要有this引用:
在Java中,使用
this
引用的主要目的是消除变量或参数的歧义,或者在对象的方法中访问对象的实例变量或方法。以下是几种常见情况下使用
this
引用的理由:-
消除歧义:在方法中,如果存在与实例变量同名的局部变量或方法参数,为了明确指代实例变量而不是局部变量,可以使用
this
。例如:public class MyClass { private int number; public MyClass(int number) { this.number = number; // 使用this引用实例变量 } public void setNumber(int number) { this.number = number; // 使用this引用实例变量 } }
-
构造器调用:在构造方法中,如果需要调用同一类中的另一个构造方法,可以使用
this()
来实现构造器调用。这种用法通常在需要避免代码重复或提高代码可维护性时很有用。public class MyClass { private int number; public MyClass() { this(0); // 调用另一个构造方法 } public MyClass(int number) { this.number = number; } }
-
在方法中引用当前对象:在对象的方法中,可能需要引用当前对象的实例变量或方法。使用
this
可以让代码更加清晰,明确地指示当前对象。public class MyClass { private int number; public MyClass(int number) { this.number = number; } public void printNumber() { System.out.println("Number: " + this.number); // 使用this引用实例变量 } }
-
-
对象的构造及初始化:
对象的构造和初始化是面向对象编程中的重要概念。在Java中,对象的构造和初始化通常涉及到构造方法和实例变量的初始化。
构造方法(Constructor)
构造方法是一种特殊类型的方法,用于创建对象时进行初始化。在Java中,构造方法的名称必须与类名相同,并且没有返回类型(甚至不是void)。当创建一个类的新实例时,会自动调用其构造方法。如果没有明确定义构造方法,则编译器会自动生成一个默认的无参数构造方法。
示例:
public class MyClass { private int number; // 构造方法 public MyClass(int number) { this.number = number; } }
在上面的示例中,
MyClass
类有一个参数为number
的构造方法,用于初始化类的实例变量number
。实例变量的初始化:
实例变量是在类中声明的变量,每个对象都有自己的一份拷贝。在Java中,实例变量可以在声明时进行初始化,也可以在构造方法中进行初始化,或者在构造方法中调用其他方法进行初始化。
示例:
public class MyClass { private int number; private String name; // 在声明时初始化 private double value = 10.5; // 构造方法中初始化 public MyClass(int number, String name) { this.number = number; this.name = name; } // 在构造方法中调用其他方法进行初始化 public MyClass() { initialize(); } private void initialize() { number = 0; name = "Default"; } }
在这个示例中,
number
和name
实例变量分别在声明时和构造方法中进行了初始化。而value
实例变量在声明时进行了初始化。此外,还展示了在构造方法中调用其他方法进行初始化的方式。通过构造方法和实例变量的初始化,可以确保对象在创建时处于合适的状态,从而提高代码的可靠性和可维护性。
-
名字必须与类名相同
-
没有返回值类型,设置为void也不行
-
创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
-
构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
构造方法中,可以通过this调用其他构造方法来简化代码:
public class MyClass { private int number; private String name; // 构造方法1 public MyClass(int number, String name) { this.number = number; this.name = name; } // 构造方法2,通过this调用构造方法1 public MyClass() { this(0, "Default"); // 调用构造方法1 } }
注意:
-
this(…)必须是构造方法中第一条语句
-
不能形成环
默认初始化:
在Java中,当创建一个对象时,如果没有明确对对象的实例变量进行初始化,Java会对它们进行默认初始化。这确保了在对象创建时,实例变量都有一个合理的初始值。默认初始化的规则如下:
1. 对于基本数据类型(primitive types),例如int、double、boolean等,如果没有显式赋初值,则它们会被赋予一个默认值:
- 数值类型(byte、short、int、long)默认为0。
- 浮点数类型(float、double)默认为0.0。
- 布尔类型(boolean)默认为false。
- 字符类型(char)默认为’\u0000’,即NUL字符。
1. 对于对象引用类型(Object references),默认初始化为null。这意味着引用没有指向任何对象。
示例:public class MyClass { // 实例变量没有显式赋初值,将会被默认初始化 private int number; private double value; private boolean flag; private String text; public static void main(String[] args) { MyClass obj = new MyClass(); System.out.println("number: " + obj.number); // 输出: number: 0 System.out.println("value: " + obj.value); // 输出: value: 0.0 System.out.println("flag: " + obj.flag); // 输出: flag: false System.out.println("text: " + obj.text); // 输出: text: null } }
-
- 封装:
-
封装是面向对象编程的重要原则之一。
-
封装通过隐藏对象的属性和实现细节,只暴露公共接口,来保护对象的数据。
-
访问限定符:
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
比如:
public:可以理解为一个人的外貌特征,谁都可以看得到
default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
private:只有自己知道,其他人都不知道
【说明】
protected主要是用在继承中,继承部分详细介绍
default权限指:什么都不写时的默认权限
访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
// MyClass.java package mypackage; public class MyClass { public int publicField = 10; protected int protectedField = 20; int defaultField = 30; private int privateField = 40; public void publicMethod() { System.out.println("This is a public method"); } protected void protectedMethod() { System.out.println("This is a protected method"); } void defaultMethod() { System.out.println("This is a default method"); } private void privateMethod() { System.out.println("This is a private method"); } }
// AnotherClass.java package anotherpackage; import mypackage.MyClass; public class AnotherClass extends MyClass { public void accessFieldsAndMethods() { // 可访问父类的public和protected成员 System.out.println(publicField); // 输出: 10 System.out.println(protectedField); // 输出: 20 // defaultField无法访问,因为AnotherClass和MyClass不在同一包中 // System.out.println(defaultField); // 编译错误 // privateField是私有的,无法在子类中直接访问 // System.out.println(privateField); // 编译错误 // 可以访问父类的public和protected方法 publicMethod(); // 输出: This is a public method protectedMethod(); // 输出: This is a protected method // defaultMethod无法访问,因为AnotherClass和MyClass不在同一包中 // defaultMethod(); // 编译错误 // privateMethod是私有的,无法在子类中直接访问 // privateMethod(); // 编译错误 } }
-
-
包
-
包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
-
导入包
在Java中,要使用另一个包中的类,你需要使用
import
语句来导入该类。import
语句告诉编译器在哪里找到指定的类,并且允许你在代码中直接使用它,而不必使用完整的类名。下面是一个简单的例子:
假设有一个包
mypackage
,其中有一个类MyClass
:package mypackage; public class MyClass { public void doSomething() { System.out.println("Doing something..."); } }
现在,如果你想在另一个包中使用
MyClass
,你可以这样做:package anotherpackage; // 导入mypackage包中的MyClass类 import mypackage.MyClass; public class AnotherClass { public static void main(String[] args) { // 创建MyClass对象并调用其中的方法 MyClass obj = new MyClass(); obj.doSomething(); // 输出: Doing something... } }
在上面的例子中,通过
import mypackage.MyClass;
语句导入了mypackage
包中的MyClass
类。这样,在AnotherClass
中就可以直接使用MyClass
类而不需要使用完整的类名mypackage.MyClass
。在Java中,官方提供的标准类库包含了大量的类和接口,可以在编写Java程序时直接使用。这些类库通常被称为Java标准库(Java Standard Library)或Java API(Application Programming Interface),它们包含在Java开发工具包(Java Development Kit,JDK)中。
一些常用的官方包(Java标准库)包括:
-
java.lang:提供了Java语言的核心类,如基本数据类型的包装类、字符串类、异常类等。
-
java.util:包含了实用工具类,如集合框架、日期时间类、随机数生成器等。
-
java.io:用于处理输入输出流,包括文件操作、网络操作等。
-
java.net:用于网络编程,提供了各种网络相关的类和接口。
-
java.awt 和 javax.swing:用于图形用户界面(GUI)开发的类库,包含了各种组件、布局管理器等。
-
java.sql:用于数据库访问的类库,提供了对关系型数据库的支持。
-
java.nio:提供了新的I/O库,支持高性能、可扩展的I/O操作。
等等。
你可以在Java官方文档中找到完整的API文档,其中包含了每个官方包中的类、接口、方法以及详细的说明。这些文档可以在Oracle官方网站上找到,也可以在Oracle JDK的安装目录下的
docs/api
文件夹中找到。要使用官方包中的类,你只需要按照导入的方式导入即可。例如,要使用
java.util
包中的ArrayList
类,只需要在文件顶部使用import java.util.ArrayList;
语句即可。import java.util.ArrayList; public class Main { public static void main(String[] args) { // 创建一个ArrayList对象 ArrayList<String> list = new ArrayList<>(); // 添加元素到ArrayList list.add("Java"); list.add("Python"); list.add("C++"); // 打印ArrayList中的元素 System.out.println("ArrayList中的元素:"); for (String language : list) { System.out.println(language); } } }
在这个示例中,我们首先使用
import java.util.ArrayList;
导入了java.util
包中的ArrayList
类。然后,在main
方法中,我们创建了一个ArrayList
对象,并添加了一些元素。最后,使用增强的for循环遍历ArrayList,并将其元素打印到控制台上。这个例子展示了如何使用官方包中的类来编写Java程序。 -
-
-
static成员:
静态成员是指属于类本身而不是实例的成员。在Java中,可以使用
static
关键字来声明静态成员,包括静态变量和静态方法。静态成员属于类级别,而不是实例级别,因此可以通过类名直接访问,而无需创建类的实例。以下是一个简单的示例,演示了静态成员的使用:
public class MyClass { // 静态变量 public static int staticVar = 0; // 静态方法 public static void staticMethod() { System.out.println("This is a static method"); } public static void main(String[] args) { // 访问静态变量和静态方法 System.out.println("Static variable: " + MyClass.staticVar); // 输出: Static variable: 0 MyClass.staticMethod(); // 输出: This is a static method // 修改静态变量的值 MyClass.staticVar = 10; System.out.println("Updated static variable: " + MyClass.staticVar); // 输出: Updated static variable: 10 } }
使用静态成员时,有几个需要注意的事项:
-
静态成员属于类:静态成员属于类本身,而不是类的实例。因此,它们可以通过类名直接访问,无需创建类的实例。
-
静态成员在内存中只有一份:静态成员在内存中只会有一份拷贝,无论创建了多少个类的实例。这意味着所有实例都共享相同的静态成员。
-
静态成员可以修改为全局状态:由于静态成员的全局性质,它们可能会修改类的全局状态。因此,在设计静态成员时要小心,确保不会导致意外的状态修改。
-
静态成员可以在没有实例的情况下使用:由于静态成员属于类,它们可以在没有创建类的实例的情况下使用。这使得静态方法可以直接被调用,而不需要先创建类的实例。
-
静态成员不能访问非静态成员:静态成员不能直接访问非静态成员,因为非静态成员属于类的实例,而静态成员则不是。如果需要访问非静态成员,必须先创建类的实例。
-
静态成员的初始化顺序:静态成员在类加载时被初始化,且初始化顺序是按照它们在代码中的声明顺序进行的。因此,在使用静态成员时要注意其初始化顺序。
-
静态成员的线程安全性:由于静态成员是全局共享的,因此在多线程环境下使用时要注意线程安全性。对于可能被多个线程同时访问和修改的静态成员,需要采取同步措施以确保线程安全。
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
-
就地初始化
就地初始化指的是:在定义时直接给出初始值
public class Student{ private String name; private String gender; private int age; private double score; private static String classRoom = "Bit306"; // ... }
-
静态代码块初始化
-
-
代码块:
在Java中,代码块是一段被包围在一对花括号
{}
中的代码。根据其位置和修饰,Java中的代码块分为以下几种类型:-
普通代码块:
位于方法体内部的代码块。普通代码块没有任何修饰,用于组织代码和限定变量的作用域。
public class MyClass { public void myMethod() { // 普通代码块 { int x = 10; System.out.println("x: " + x); } // x在此处不可访问 } }
-
静态初始化块:
用
static
关键字修饰的代码块,在类加载时执行,并且只执行一次。通常用于静态变量的初始化。public class MyClass { static { System.out.println("Static Initialization Block"); } public static void main(String[] args) { // 这里执行类加载,会触发静态初始化块的执行 System.out.println("Inside main method"); } }
-
实例初始化块:
没有使用
static
关键字修饰的代码块,每次创建类的新实例时都会执行。用于对实例变量进行初始化。public class MyClass { { System.out.println("Instance Initialization Block"); } public static void main(String[] args) { MyClass obj1 = new MyClass(); // 创建实例1,执行实例初始化块 MyClass obj2 = new MyClass(); // 创建实例2,执行实例初始化块 } }
注意事项
-
静态代码块不管生成多少个对象,其只会执行一次
-
静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
-
如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
-
实例代码块只有在创建对象时才会执行
-
-
内部类:
在Java中,内部类是定义在另一个类内部的类。内部类的主要作用是将一些逻辑上相关的类组织在一起,并且可以访问其外部类的成员,包括私有成员。内部类的种类有多种,包括成员内部类、静态内部类、局部内部类和匿名内部类。
实例内部类(Member Inner Class)
成员内部类是定义在另一个类内部的普通类,它可以访问外部类的成员(包括私有成员)。
javaCopy code public class OuterClass { private int outerField; public OuterClass(int outerField) { this.outerField = outerField; } // 实例内部类 public class InnerClass { public void display() { System.out.println("OuterField: " + outerField); } } public static void main(String[] args) { OuterClass outerObj = new OuterClass(10); OuterClass.InnerClass innerObj = outerObj.new InnerClass(); innerObj.display(); // 输出: OuterField: 10 } }
【注意事项】
-
外部类中的任何成员都可以在实例内部类方法中直接访问
-
实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
-
在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
-
实例内部类对象必须在先有外部类对象前提下才能创建
-
实例内部类的非静态方法中包含了一个指向外部类对象的引用
-
外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
静态内部类(Static Nested Class)
静态内部类是定义在另一个类内部的静态类,它不能直接访问外部类的非静态成员,但可以通过创建外部类的对象来访问。
javaCopy code public class OuterClass { private static int outerStaticField; static class StaticInnerClass { public void display() { System.out.println("OuterStaticField: " + outerStaticField); } } public static void main(String[] args) { OuterClass.StaticInnerClass innerObj = new OuterClass.StaticInnerClass(); innerObj.display(); // 输出: OuterStaticField: 0 } }
【注意事项】
- 在静态内部类中只能访问外部类中的静态成员
如果确实想访问,我们该如何做?
- 创建静态内部类对象时,不需要先创建外部类对象
局部内部类(Local Inner Class)
局部内部类是定义在方法内部的类,它只能在定义它的方法内部访问,不能被其他方法或外部类访问。
javaCopy code public class OuterClass { public void display() { int localVar = 10; // 局部内部类 class LocalInnerClass { public void display() { System.out.println("LocalVar: " + localVar); } } LocalInnerClass innerObj = new LocalInnerClass(); innerObj.display(); // 输出: LocalVar: 10 } public static void main(String[] args) { OuterClass outerObj = new OuterClass(); outerObj.display(); } }
【注意事项】
-
局部内部类只能在所定义的方法体内部使用
-
不能被public、static等修饰符修饰
-
编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class
-
几乎不会使用
-
-
对象的打印:
在Java中,要将对象的信息打印到控制台上,你可以使用对象的
toString()
方法。toString()
方法返回一个包含对象的字符串表示的字符串。默认情况下,
Object
类中的toString()
方法返回的是一个字符串,该字符串包含了对象的类名(包括包名)以及该对象的哈希码的十六进制表示。通常,我们可以通过重写toString()
方法来返回更有意义的对象信息。以下是一个示例,展示了如何重写
toString()
方法来自定义对象的打印信息:javaCopy code public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // 重写toString()方法 @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } public static void main(String[] args) { Person person = new Person("Alice", 30); System.out.println(person); // 输出: Person{name='Alice', age=30} } }
在这个示例中,
Person
类重写了toString()
方法,以返回包含对象的名称和年龄的字符串表示。然后在main
方法中,我们创建了一个Person
对象,并将其打印到控制台上,使用了System.out.println(person)
语句。由于我们重写了toString()
方法,因此输出的是自定义的字符串表示形式。