目录
2.1 、类的编写
类(Class)是封装对象属性和行为的载体,是一个用于创建实例对象的模板,它将对象的共同特征和行为抽象出来,用成员变量表示事物的属性,用方法描述具体的行为,从而形成类。而通过new关键字对类实例化,即是创建了具有这种特征和行为的一个对象。
通常类具有五大成员:属性(成员变量)、方法、构造器、代码块、内部类。
package **; //类所在的包
import **; //导入其他的类
声明一个Person类,public为权限修饰符表示都可以访问,class关键字表示这是一个类,Person是类名
public class Person{
/**
* 类的属性(成员变量),一个人有哪些特征。人有姓名、有年龄、有性别........
*/
private String name;
private int age;
private char gender;
.........
/**
* 构造函数,每个类都有一个默认的无参构造函数,也可以自己定义,定义后默认的无参构造就不存在了
* 构造函数可以用来对属性进行初始化
*/
public Person(){}
public Persion(String name, int age,char gender){
this.name = name;
this.age = age;
this.gender = gender;
}
/**
* 实例代码块,也可以用来对属性进行初始化,它在创建对象时,在构造方法执行前执行
*/
{
name = "张三";
age = 22;
gender = '男';
}
/**
* 内部类,在类中再定义一个类,此类作为外部类的一个组成部分
*/
public class 类名{
}
/**
* 成员方法,表示人可以说话
*/
public void say(){
System.out.printf("我叫%s,今年%d岁,性别%s",name,age,gender);
}
}
2.1.1、变量
1、成员变量
Java对象的属性即是定义在类内部,方法外部的变量,其中通过static修饰的属性为类变量(final+static修饰的称为常量),没有static修饰的属性为成员变量。类的属性可以使任意数据类型。
- 类变量:由关键字static修饰,属于类,它在类被JVM加载时就分配空间,并指定默认的初始值,可以在不创建对象的情况下直接通过类名调用(对象也可以调用类变量,但是不推荐这么做,类变量最好还是通过类来调用)
- static修饰:静态变量,可以不赋初值, 有默认值。在类加载的准备阶段先赋默认值,初始化阶段再赋正确值
- static final修饰:是常量,必须赋初值,在类加载的准备阶段会直接赋正确值
- 实例变量:没有static修饰的,属于对象,可以不赋初值, 有默认值。只有在类实例化为对象时,才会分配空间,通过对象名调用
//属性的定义
权限修饰符 数据类型 变量名 = 值; //成员变量
private int num = 15;
权限修饰符 static 数据类型 变量名 = 值; //类变量
public static String str ;
权限修饰符 static final 数据类型 变量名 = 值; //常量
public static final double PI = 3.14;
2、局部变量
在 Java 中,局部变量是定义在方法、构造器或代码块中的变量。它们只在定义它们的区域内可见可用。
- 声明:局部变量在使用前必须声明。声明时,需要指定变量的类型和名称。不能被权限修饰符修饰
- 初始化:在使用局部变量之前,必须显式的初始化初值。Java 编译器不会给局部变量一个默认值。
局部变量生命周期从它被初始化(赋值)时开始,到包含它的块执行完毕时结束。当块执行完毕后,局部变量所占用的内存将被回收。
- 形参:定义在方法、构造器的()中,在整个方法内部都有效
- 方法局部变量:定义在方法内部,方法运行至局部变量赋值那刻开始生效,到方法执行结束时失效
- 代码块局部变量:定义在代码块内,代码块运行至局部变量赋值那刻开始生效,到代码块执行结束时失效
public class Test {
public void method() {
int num = 10; // 局部变量声明和初始化
System.out.println(num); // 使用局部变量
}
public void anotherMethod(String name) { //参数
// System.out.println(num); // 错误:num是定义在method方法内的,在这里不可见
}
}
局部变量和成员变量可以同名,如果方法中局部变量和成员变量同名时,局部变量会覆盖成员变量(就近原则),如果还想使用成员变量,需要配合this关键字。
3、区别
在Java中,成员变量和局部变量是两种常见的变量类型,它们在作用域、生命周期、初始化行为等方面有着明显的区别。
-
定义位置
-
成员变量:字段(field),定义在类级别。可以是静态的(属于类)或非静态的(属于类的实例)。
-
局部变量:定义在方法内、方法参数或者代码块内。只在定义它们的方法或代码块中有效。
-
-
初始化值
-
成员变量:如果没有明确地初始化,Java会为它们提供一个默认值。
-
局部变量:必须在使用前显式初始化,否则编译器会报错。Java不为局部变量提供默认值。
-
-
存储位置
-
成员变量:存储在堆内存中,属于对象的一部分,而对象是在堆上分配的。
-
局部变量:通常存储在栈内存中,访问速度很快。
-
-
生命周期
-
成员变量:
-
实例变量:生命周期与对象的生命周期相同。
-
静态变量:其生命周期从类被加载开始到应用程序结束时为止。
-
-
局部变量:生命周期仅限于方法调用的过程中。方法调用完成,局部变量所占用的内存就会被释放。
-
-
作用域
-
成员变量
-
实例变量:作用域是整个类内部,可以被类中的所有方法、构造器和特定的代码块访问。
-
静态变量:可以被类的所有实例共享。
-
-
局部变量:作用域仅限于声明它们的方法或代码块内。离开这个作用域后,它们将不再可用。
-
-
多线程
-
成员变量:如果多个线程访问同一个对象的实例变量,可能会遇到并发问题。
-
局部变量:因为每次方法调用都会为局部变量创建新的内存空间,所以局部变量本身是线程安全的。
-
4、新特性:var
JAVA 10后的一种新特性:使用var 定义局部变量,作用看起来类似与JavaScript 或 Python(实际上是不相同的),允许编译器利用其初始值来推断局部变量的类型。
- Java是一种强语言,所以在定义变量时需要指定数据类型,而 Java10提供的var相当于一个动态类型,它定义的类型会由编译器自动推断出其初始的数据类型。
- JavaScript 或 python属于弱类型语言,所以它们使用var时并没有明确的类型,但是Java即便使用var依然有明确的类型,它在指定初始值时,类型就确定下来了。
使用var时需要注意以下几点:
- var不是关键字,而是一个保留类型名,所以可以在代码中使用 var 作为变量名,但是不推荐这么做
- 使用 var 时,必须初始化变量,因为 var 需要通过变量的初始值来推断变量的类型(不能初始化为NULL值)
- 只能用于局部变量,不能用于类的成员变量、方法参数或返回类型。
- 不能使用 var 来推断泛型方法调用的类型参数。
- var不建议大范围使用,他会降低代码的可读性,建议在局部变量且初始值很容易确定的时候使用。
2.1.2、方法
方法是类中用来完成特定功能的代码片段,它是封装起来的一段段处理数据的逻辑代码,通过方法处理数据,得到最后的结果。它们是类和对象行为的核心部分。通过调用方法,可以实现代码的重用,提高程序的可维护性和可读性。
方法不能独立存在,必须属于类或者对象,在执行时由对象或类来调用(静态方法有类调用,非静态方法由对象调用)
方法一般为:构造方法、普通方法、main方法。其中main方法是程序的入口,由JVM调用。普通方法需要程序员去调用。构造方法是用来对一些属性等数据做一个初始化的处理,它是在new时被调用。
之所会设置方法这种结构,是为了解决软件开发中的几个重要问题:
封装和抽象:在没有方法的情况下,所有的逻辑都会堆积在一起,使得代码难以理解和维护。
代码重用:在软件开发中,经常会遇到需要重复执行相同任务的情况。没有方法,开发者可能会在多个地方复制和粘贴相同的代码,这导致代码冗余和维护困难。
模块化和组织:随着程序规模的增长,没有清晰的结构,代码很快就会变得混乱无章。
可维护性和可测试性:在大型软件项目中,确保代码的质量和寻找错误变得非常困难。没有一种有效的方式来隔离和测试代码的不同部分,测试和维护工作会变得非常耗时。
1、main方法
java中的main函数也叫作主函数,它主要作用一个程序的入口,可以运行,会被JVM识别并自动加载。格式是固定的:
public static void main(String[] args) {
}
public static void main(String[] 名字) 这个格式是固定的。
- public static:程序在运行时执行的就是main方法,JVM通过 main 找到需要启动的运行程序,并检查main所在的类是否被加载,如果没有就进行加载,因此需要设置为 public static
- void:main方法在执行完成后,程序运行就结束了,所以不需要返回值
- String[](jdk5后也可以使用动态参数):用来运行时接受用户输入的参数。
main方法其实本质上也是一个 static 修饰的静态方法,除了还能作为程序的入口外,其他特性和普通静态方法一样。
2、构造方法
Constructor(构造器) 是类中一种特殊的方法,它只在对象创建的时候执行一次,用来进行初始化的一些操作
,类默认会有一个无参的构造方法(类至少都有一个构造器),也可以自己定义,但是一旦自己定义,默认的无参构造方法就不存在了,主要是方便对象的初始化
。构造方法可进行重载,在同一个类中,通过传递的参数不同而选择相应的构造方法。
public class Test{
public Test(){
//这就是构造方法
}
}
- 特点
- 方法的名字和类名完全相同,每个类有1个以上的构造方法,可以有0个以上参数。
- 仅在创建对象时执行一次
- 每个类会有一个默认的无参构造方法(前提是类没有任何其他构造器的时候),一旦自己定义了构造方法,默认的就不存在了
- 默认无参构造方法在初始化时会把实例变量初始化为默认值,而有参构造会把实例变量按给定的参数值进行实例化。
- 无返回值,也不能用void关键字(实际上构造方法返回的就是创建的实例对象)
- 权限如果为私有,可以提供静态方法,将类创建为单例模式
- 不能被static、final、synchronized、abstract、native修饰,不能有 return语句返回值
- 作用
- 结合new关键字创建实例对象,并初始化实例变量
- 调用父类的构造方法
- 分配内存空间存储对象,并返回指向该空间的引用
- 执行其他操作
public class Student{
private String name;
//构造方法。无返回值,方法名字和类型相同
public Student(String name){
//在构造方法中对属性进行初始化操作
this.name = name;
}
//定义了有参的构造方法,还想用无参的构造,就必须手动定义(构造器的重载)
public Student(){
}
}
构造方法是一种特殊的方法,因为必须和类名一致,子类是无法继承父类构造方法的,同时因为子类在创建实例时会先初始化父类的构造方法,所以在Java中的会有一个默认无参的构造方法,同时我们在创建类如果需要写有参构造方法,我们最好再提供一个无参的构造方法,方便类进行初始化的操作。
![image-20220831153909097](https://img-blog.csdnimg.cn/img_convert/163908578b12a3239c4666cb7f73ae4e.png)
3、成员方法
主要用来定义对象的行为或操作,使得对象可以执行特定的任务,它们是对象如何响应外部请求的主要方式。
-
方法的定义
//普通方法 [修饰符] 返回值类型 方法名(参数列表){ //无返回值为void,有返回值则为相应的数据类型 方法体; return 返回值; //有返回值时,使用关键字return返回 } /** 1、修饰符:是可选的,可用的修饰符为public、protected、private、static、final、abstract 2、返回值类型 :方法可能会有返回值。也可能没有返回值。有返回值时表明返回值类型,并使用return返回。没有时使用void关键字 3、参数类型:调用方法时传递给方法的参数,此参数可以在方法体中使用。调用方法时传递的参数顺序要要一致 4、方法体:方法体包含具体的语句,定义该方法的功能。 5、关键字return:结束方法,返回方法的返回值(有返回值时不能使用void) *
-
方法的调用:如果存在方法重载,即多个同名但参数不同的方法时,则根据传递的参数类型或个数不同而匹配不同的方法
- 静态方法:static修饰的方法,类加载时就会产生(静态方法调用非静态方法时,需要先创建对象)
- 类名.方法名();
- 非静态方法:对象创建时才会产生(非静态方法可以直接调用静态方法)
- 先实例化对象:类名 对象名 = new 类名();
- 对象名.方法名();
- 静态方法:static修饰的方法,类加载时就会产生(静态方法调用非静态方法时,需要先创建对象)
4、方法递归
方法有一种自己调用自己的操作(还有一种是间接的:比如方法A调用方法B,方法B调用方法C,方法C再调用方法A),这种方式就是递归,通过自己调用自己从而形成一种循环的结构,所以和循环类似。
在使用递归时,首先我们要知道使用递归来解决什么问题
,其次明确递归的结束条件
,不能使其变成无限递归的死循环,最后要总结递归的等价关系式
。(构造方法禁止使用递归)
递归的应用场景:
-
阶乘问题
public static void main(String[] args) { // 5的阶乘 System.out.println(factorial(5)); } /* * @description 使用递归解决阶乘问题 */ public static int factorial(int n){ // 结束条件 if (n == 1) { return 1; } //阶乘的关系式 return n * factorial(n - 1); }
-
二叉树深度
-
汉诺塔问题
-
斐波那契数列:1、1、2、3、5、8、13、21、34、55…
public static void main(String[] args) { //斐波那契数列的每个数都是前两个数的和,除了前两个数。 //F(0)=1, F(1)=1 F(n)=F(n−1)+F(n−2) for(int i=1 ; i<=10; i++){ System.out.print(fibonacci(i)+","); } } /* * @description 斐波那契数列 */ public static int fibonacci(int n) { // 设置结束递归的限制条件 if (n <= 2) { return 1; } // 递归步骤 return fibonacci(n - 1) + fibonacci(n - 2); }
-
快速排序、归并排序(分治算法体现递归)
-
遍历文件、解析xml文件等
public static void main(String[] args) { //递归获取文件夹的内容 File f = new File("d:/mydata"); printFile(f); } public static void printFile(File file) { // 获取指定文件夹内的所有文件和文件夹 File[] files = file.listFiles(); //先判断该文件是否正确 if(files != null){ //如果是文件,直接打印绝对路径. //如果是文件夹,则使用递归,获取该文件夹内的文件和文件夹 for (File f : files) { // 是文件,直接打印 if (f.isFile()) { System.out.println("[" + f.getAbsolutePath()+"]"); } else { // 是文件夹:打印出文件夹后,使用递归获取文件夹内的内容 System.out.println("目录:" + f.getAbsolutePath()); printFile(f); } } }else { System.out.println("该路径不存在!!!!!"); } }
-
………….
5、参数
方法的()里接收的就是参数,它是一种将值或变量引用传递给函数或方法的机制。当一个方法被定义时,它可以被设计为接收参数,这些参数在方法执行时提供必要的输入数据。
-
基本类型参数:传递的参数为基本数据类型
-
引用类型参数:传递的参数为引用数据类型
-
变长参数(Varargs):在JDK5之后,还有一种可变的参数,它允许在调用方法时传入任意数量的参数。
//传递可变参数,在方法中使用时和数组类似 public void printMultipleNumbers(int... numbers) { for (int number : numbers) { System.out.println(number); } }
-
泛型参数:可以定义泛型参数,这样的参数类型在调用方法时才指定,提高了代码的重用性。
//泛型,具体类型需要传递调用时确定 public <T> void printElement(T element) { System.out.println(element); }
-
Lambda表达式作为参数:Java 8 引入了Lambda表达式,可以传递一个Lambda表达式作为参数,通常是为了满足某个函数式接口的契约。
public void useThread(Runnable action) { new Thread(action).start(); }
2.1.3、代码块
在Java中,使用 { } 括起来的代码被称为代码块,但是代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,是在加载类或创建对象的时候隐式调用(具体在JVM类加载的初始化阶段)。
代码块按定义的先后顺序依次执行,内部可以包括任何可执行语句:局部变量、调用其他对象方法、循环语句等等。
[修饰符] {
代码块中的代码
}
根据代码块声明的位置和修饰符的不同,可以分为:
-
局部代码块:定义在方法中的代码块,也叫本地代码块,比较少见。可以限定变量生命周期,及早释放,提高内存利用率。调用其所在的方法时执行
-
静态代码块:在类中由static修饰,一般用于初始化静态成员变量,它随类加载而执行,并且只会执行一次。
-
实例代码块:定义在类中的非静态代码块,用于初始化实例成员变量。在构造方法前执行(创建对象时执行)。
实例代码块是在创建对象时按顺序隐式执行,所以多个代码块尽量合成一个,这样可以提高可读性
![image-20230426191442623](https://img-blog.csdnimg.cn/img_convert/e9364912b533f2ca90c818ccdd7d0844.png)
-
同步代码块:synchronized(obj){ } 内的代码块,一种多线程保护机制,被Synchronized关键词修饰的代码块会被加上内置锁,只有获得了这把锁的线程才访问,并且同一时刻,只有一个线程能持有这把锁,这样就保证了同一时刻只有一个线程能执行被锁住的代码。
synchronized(Object) { // 由锁保护的代码 }
实例变量的初始化顺序:先执行实例初始化块或直接给实例变量执行初始值(这两种优先级相同,定义在后面的会覆盖掉定义在前面的),在执行在构造器中制定的初始值。
实例初始化块在前,直接赋值在后,构造方法不指定初始值
![]()
直接赋值在前,实例初始化块在后,构造方法不指定初始值
![]()
三者都写
![]()
2.1.4、 内部类
一个类定义在另一个类内,那么这个类就是一个内部类(需要定义在类的 { } 内部才行,写在同一个源文件中互相独立的类并不是内部类),比如:在类A中定义一个类B,B就是内部类,而B可以当做A的一个成员看待。从种类上说,内部类可以分为四类:成员内部类、静态内部类、匿名内部类、局部内部类。
1、成员内部类
成员内部类:最普通的一种内部类,定义在类中方法外的一个普通的类(没有static修饰),可用任意的访问修饰符修饰
-
内部类可以访问外部类所有的属性和方法(包括私有)。但是外部类要访问成员内部类的属性和方法,必须要先实例化内部类
-
在其他的类中使用内部类时,需要先有外部类的对象(成员内部类是依附外部类而存在的)
-
关于在成员内部类中使用静态方法和属性的问题: JDK16后可以在非静态内部类中使用静态属性和方法的说明
-
在 jdk16之前的版本,不能在非静态成员内部类中定义静态的属性和方法
-
在Java16之后的版本是可以定义静态方法和属性的
-
-
非静态内部类的方法访问变量时,首先在方法内查找是否存在该名字的局部变量,存在就使用,不存在则在内部类中查找是否存在该名字的成员变量,存在就使用,不存在则去外部类中找该名字的成员变量,存在就使用,不存在会出现错误,提示该变量不存在。
当外部类变量与方法和内部类的同名时,内部类默认访问自己的成员变量或方法,而要引用外部变量时要加类名.this(类名.this.变量名、类名.this.方法名())
2、静态内部类
静态内部类:静态内部类就是在成员内部类加了一个 static 关键字,静态内部类就像外部类的一个静态成员一样,属于类而非对象。
-
静态内部类不能直接访问外部类的非静态成员,但可以通过创建外部类的对象访问。
-
外部类可以通过类名调用静态成员,或者使用静态内部类的对象访问内部类的实例成员
-
外部类的静态成员与内部类的静态成员名称相同时,可通过(外部类名.静态成员) 访问外部类的静态成员
-
静态内部类创建对象无需依赖外部类对象,可以直接创建
3、局部内部类
局部内部类:使用的比较少,声明在方法体或一段代码块的内部,仅在方法或代码块中生效。局部内部类不能再外部类方法以外的地方使用,所以局部内部类不能使用访问控制符和static 修饰。
-
仅在定义的方法、代码块内部使用,其他地方无法使用(定义在方法内则只能在方法中使用,定义在for循环中则只能在for循环中使用),作用范围类似于局部变量
-
局部内部类不可使用权限修饰符、静态(static)修饰符进行修,但可以使用final 或 abstract修饰。
-
局部内部类可以直接访问方法中的属性
-
局部内部类创建对象要在方法内部、局部内部类的外部声明。
-
可以访问外部类的的属性和方法(包括私有的)。
4、匿名内部类
匿名内部类:很常用。它是一种没有名字的内部类,只能使用一次,通常是某个类在实现时只会使用一次,所以为了简化,不需要去专门写一个实现类,而是直接使用内部类完成。
- 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
- 在匿名内部类中可以使用外部类的属性,但是外部类却不能使用匿名内部类中定义的属性,匿名内部类没有名字,因此在外部类中无法获取这个类的类名,也就无法得到属性信息。(只能有一个对象)
- 匿名内部类必须继承父类或实现接口,使用多态形式引用
new 接口/类名(参数1, 参数2...){
实现方法1(){
}
实现方法2(){
}
......
};
在线程的时候通常就喜欢使用匿名内部类实现
![image-20220724171746400](https://img-blog.csdnimg.cn/img_convert/122bd84e2a4301161157d166b1144db8.png)