一、类和对象的基本概念
Java是什么?Java是一门面向对象的编程语言(OOP),想要理解Java,首先要理解类(Class)和对象(Object)这两个概念。
类:把具有相同属性和行为的一类对象抽象为类。类是抽象概念,如人类、犬类等,无法具体到每个实体。
对象:某个类的一个实体,当有了对象后,这些属性便有了属性值,行为也就有了相应的意义。
类是描述某一对象的统称,对象是这个类的一个实例而已。有类之后就能根据这个类来产生具体的对象。一类对象所具备的共同属性和行为(方法)都在类中定义。
二、类与对象的定义与使用
1.创建类的语法:
class Person {
public int age;//成员变量 实例变量
public String name;
public String sex;
public void eat() {//成员方法
System.out.println("吃饭!");
}
public void sleep() {
System.out.println("睡觉!");
}
void print(){
System.out.println("name:"+name+", age:"+age+", sex:"+sex);
}
}
2. 创建具体的对象:
类名称 引用名称 = new 类名称()
public class Test {
public static void main(String[] args) {
//创建一个实例化对象
Person per1 = new Person();
//通过对象来调用实例变量、成员方法
per1.name = "小韩";
per1.age = 18;
per1.sex = "女";
per1.print();
Person per2 = new Person();
per2.print();
}
}
输出结果:
可以看到成员变量在没有赋值之前是会默认赋值为类型相对应的0值。
类型 | 对应的0值 |
---|---|
整型 | 0 |
浮点型 | 0.0 |
字符类型r | /u0000 |
引用类型 | null |
布朗类型 | false |
3. 总结:
1、在Java中,一个 .java
文件可以包含多个类,但只能有一个公共类(public class)。也就是说,一个 .java
文件中可以有多个类,但只能有一个类被声明为 public,且文件名必须与这个 public 类的类名相同。
2、类的命名规则:使用有意义的大驼峰单词命名法,每个单词的首字母都要大写
3、类中定义的成员变量都有默认值,而局部变量需要自己赋值才能使用。
4、关于引用数据类型的特殊值 null:null在Java中表示“空引用”,即只有名字,没有任何对内存中的地址,如果直接使用值为null的引用,去操作(使用.操作符)任何属性或者方法(成员变量、成员方法),都会报错。 如:空指针异常
三、static关键字
1. static修饰属性(类属性、类变量)
(1) 所谓的类变量也就是静态变量,由关键字static修饰,存储在方法区,在类加载的时候就会分配空间,所以类变量是脱离对象的,就是说,就算没有实例化对象也是可以使用类变量的,看下图,没有对象依然可以通过 类名.变量名 使用类变量。
public class staticTest {
public static void main(String[] args) {
Person per1 = new Person();
Person per2 = new Person();
per1.name = "小韩";
per1.age = 18;
per1.print();
per2.print();
Person.country="日本";
per1.print();
per2.print();
System.out.println(Person.country);
}
}
class Person {
//实例变量,成员方法,必须通过该类的对象来访问
String name;
int age;
String sex;
//静态成员变量,不属于对象,属于类,保存在方法区中
// 调用时通过 类名称.country来进行访问
static String country = "中国";
void print(){
System.out.println("name:"+name+", age:"+age+", country:"+country);
}
}
输出结果:
2. static修饰方法(类方法、静态方法)
(1)由static修饰的方法也是类方法也叫静态方法,它和类变量具有相同的性质,类加载时分配空间,不依赖于实例化的对象,可以直接通过 类名.函数名() 调用。
如果不是静态的变量和方法,那么我们就需要先通过new关键字实例化一个对象才可以使用。
3. 注意事项
问题1:能否在方法中定义一个static变量?
不能。在方法中定义的变量是局部变量,在栈中存储,而 static变量是在方法区中存储,若要在方法中定义一个static变量就会产生矛盾,因为一个变量不可能既在栈中存储,又在方法区中存储。
问题2:为什么static 方法是一个静态方法?
因为主方法是程序的入口,在进入主方法前没有对象, 如果主方法是一个成员方法,需要通过对象调用,就会产生矛盾。要使程序开始执行,只有通过类名称直接调用静态方法,无须产生对象。
问题3:静态方法能否访问成员变量和成员方法?
不能。 因为非静态成员是和对象绑定的,而静态方法是和类绑定的,不存在对象实例,无法访问非静态成员。如果在静态方法中需要访问成员变量或成员方法,可以通过创建对象实例来实现。具体来说,需要先创建一个对象实例,然后通过该实例来访问成员变量或成员方法。
需要注意的是,如果在静态方法中频繁地创建对象实例来访问成员变量和成员方法,可能会导致性能问题。因此,应该尽量减少在静态方法中对成员变量和成员方法的访问。
问题4:成员方法能否访问静态变量和静态方法?
可以。 因为在类加载的时候,静态变量和静态方法已经被加载到内存中并分配了内存空间,可以被整个类共享访问。具体来说,在成员方法中可以通过类名或对象名(推荐使用类名)来访问静态变量和静态方法。
四、面向对象的特性
面向对象一共有三大特性:封装、继承和多态(本篇博客主要讨论封装性)
封装:保护性和易用性。通过访问修饰符来实现。
一共有四大访问修饰修饰符,它们的权限大小顺序是:
public
(公共访问权限):被修饰的类、方法和变量可以被任意其他类访问,没有访问限制,即可以在任意位置被访问。
protected
(受保护的访问权限):被修饰的类、方法和变量可以被同一个包内的其他类访问,同时也可以被不同包中的子类访问,即在同一个包内或者不同包中的子类中可以访问。
default
或不加修饰符(默认访问权限):被修饰的类、方法和变量可以被同一个包内的其他类访问,但不能被不同包中的类访问。
private
(私有访问权限):被修饰的类、方法和变量只能被自己所在的类访问,其他类无法访问。因此,权限大小的顺序是:public > protected > default > private。一个被限定为
private
的成员只能被其所在类的方法所访问,而一个被限定为public
的成员可以被任意其他类访问。需要注意的是,这里的访问限制是针对类、方法和变量的访问权限,不同于实例变量和静态变量之间的区别。
//公共访问权限,主类
public class privateTest {
public static void main(String[] args) {
Bank bank = new Bank();
bank.setPassword();
System.out.println("修改后的密码为:");
System.out.println(bank.getPassword());
System.out.print("银行卡号为:");
System.out.println(bank.getCardNum());
System.out.print("银行卡余额为:");
System.out.println(bank.getBalance());
}
}
//缺省修饰符,包访问权限
class Bank{
//私有属性,只在Bank这个类内部可见
private int cardNum;//卡号,只可取
private double balance;//余额,只可取
private String password = "123456";//密码,既可取也可改
//要在类的外部去使用这些私有属性,需要使用类提供的getter()和setter()方法
//alt + insert 快速生成getter()和setter()方法
//shift+A全选
//get + 属性名称 = 方法命名
public int getCardNum() {
return cardNum;
}
public double getBalance() {
return balance;
}
public String getPassword() {
return password;
}
//设置新密码
public void setPassword() {
//验证旧密码(安全性)
Scanner scan = new Scanner(System.in);
int count = 0;
while(true){
System.out.println("请输入您的旧密码:");
String oldPass = scan.nextLine();
count ++;
if (oldPass.equals(password)) {
System.out.println("验证成功,请输入您的新密码:");
String newPass = scan.nextLine();
password = newPass;
System.out.println("密码修改成功");
break;
}else{
System.out.println("密码错误,请查证后再试!");
if(count == 3){
System.out.println("验证次数过多,银行卡已锁定");
break;
}
}
}
}
}
问题6: private 关键字能否修饰一个类(外部类)?
在 Java 中,
private
关键字只能用来修饰成员变量、成员方法和内部类,不能用来修饰外部类。因此,private
不能用来限制一个类(外部类)的访问权限,只能用来限制该类的成员变量、成员方法和内部类的访问权限。
五、构造方法
1、在 Java 中,构造方法(Constructor)是一种特殊的方法,用于在创建对象时初始化对象的状态。构造方法的名称必须与类名相同,且没有返回类型(包括 void
),因为构造方法的主要作用是创建对象,而不是返回某个值。
2、每当创建一个对象时,Java 都会自动调用该对象的构造方法来初始化对象的状态。构造方法可以接受参数,以便在创建对象时提供初始值,也可以不接受参数,以便使用默认值来初始化对象的状态。如果一个类没有定义构造方法,则 Java 会为该类生成一个默认的无参构造方法,该构造方法不做任何操作,只是为了保证该类可以被创建对象。(当类中自定义了构造方法,默认的无参构造就不再生成。)
3、构造方法的意义在于,它可以确保在创建对象时,对象的状态能够正确地被初始化。如果没有构造方法,那么对象的状态可能会处于未定义的状态,从而导致程序出现不可预测的行为。构造方法还可以对参数进行验证和处理,以确保传入的参数符合对象的要求。同时,构造方法还可以在对象创建时执行一些初始化操作,例如打开文件、连接数据库等。
需要注意的是,构造方法可以重载,即一个类可以定义多个构造方法,只要它们的参数列表(个数)不同即可。这样可以为对象的创建提供更多的灵活性,例如可以根据不同的参数来创建不同的对象。
public class Test {
public static void main(String[] args) {
//当构造对象时,默认调用该类的构造方法
Person person = new Person();
Person person1 = new Person("小韩");
Person person2 = new Person("小韩",18);
Person person3 = new Person("小韩",18,"女");
}
}
class Person{
String name;
int age;
String sex;
public Person(){
//构造方法首行name = null,age = 0;sex = null(不用写)
System.out.println("Person类的无参构造");
}
public Person(String n){
//首行name = null,age = 0;sex = null;
name = n ;
System.out.println("name:"+name);
System.out.println("Person类的一个参数的有参构造");
}
public Person(String n,int a){
name = n ;
age = a;
System.out.println("name:"+name+", age:"+ age );
System.out.println("Person类的两个参数的有参构造");
}
public Person(String n,int a,String s){
name = n ;
age = a;
sex = s;
System.out.println("name:"+name+", age:"+ age +", sex :"+sex);
System.out.println("Person类的三个参数的有参构造");
}
}
问题7:能否用一个实例化对象去调用它的构造方法?
在 Java 中,不能使用实例化对象来直接调用构造方法。构造方法只能在创建对象时被调用,它的主要作用是对新创建的对象进行初始化操作。因此,构造方法只能在
new
操作符后面被调用,而不能像普通方法一样被实例化对象调用。
4、构造方法的生命周期主要包括两个阶段:对象的分配、对象的初始化。
- 对象的分配阶段
在创建对象时,Java 会首先在堆(Heap)中为对象分配一块内存空间,以便在其中存储对象的数据。对象的大小取决于其所包含的成员变量的数量和类型,其中包括该类继承的成员变量和自身定义的成员变量。在分配内存时,Java 会为对象分配默认值,例如将所有数值类型设置为 0,将布尔类型设置为 false,将引用类型设置为 null 等。
2. 对象的初始化阶段
一旦分配了内存空间,Java 就会调用对象的构造方法来对其进行初始化。构造方法可以设置对象的成员变量、调用其他方法等,以便对对象进行进一步的处理和初始化。构造方法的执行顺序是从父类到子类,先执行父类的构造方法,再执行子类的构造方法。如果一个类没有显示定义构造方法,则 Java 会默认提供一个无参构造方法来初始化对象,该构造方法不做任何处理。
六、this 关键字(表示当前对象的引用)
在 Java 中,this
关键字代表当前对象,可以用于引用当前对象的成员变量和成员方法。this
关键字主要用于以下两个方面:
- 区分局部变量和成员变量
当成员变量和局部变量名称相同时,可以使用
this
关键字来区分它们。在方法中,局部变量的作用域优先级高于成员变量(就近原则),如果不使用this
关键字来引用成员变量,则方法中将使用局部变量而不是成员变量。例如,假设一个类有一个成员变量name
,并且在一个方法中定义了一个与成员变量同名的局部变量name
,那么可以使用this
关键字来引用成员变量name
:public class Person { private String name; public void setName(String name) { this.name = name; } }
在上面的代码中,
this.name
指的是成员变量name
,而不是方法中的局部变量。2. 在构造方法中调用另一个构造方法
当一个类有多个构造方法时,可以使用
this
关键字来调用其他构造方法。在这种情况下,构造方法的调用应该是构造方法中的第一条语句,以便确保对象被完全初始化。例如,假设一个类有两个构造方法,其中一个构造方法调用了另一个构造方法来完成对象的初始化:public class Person { private String name; private int age; public Person() { this("Unknown", 0); } public Person(String name, int age) { this.name = name; this.age = age; } }
在上面的代码中,无参构造方法调用了另一个带参构造方法来完成对象的初始化,避免了重复的代码。
在Java中,this
关键字代表调用当前方法或访问当前属性的对象实例。当你在一个对象的方法中使用this
关键字时,它指的就是调用该方法的当前对象。
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
System.out.println(student1);
student1.fun();
System.out.println();
Student student2 = new Student();
System.out.println(student2);
student2.fun();
}
}
class Student{
String name;
int age;
String sex;
public void fun(){
System.out.println(this);
}
}
七、代码块
代码块:指的是使用{ }括起来的一段代码,称为代码块。根据定义的代码块的位置以及关键字的不同分为以下四种代码块。
(一)普通代码块
定义在方法中,使用{ }括起来的一段代码。与方法或构造函数中的代码块不同,普通代码块不与任何特定的关键字或修饰符关联。(用法比较少见)
public class MyClass {
public void doSomething() {
System.out.println("方法内的代码块之前");
{
int x = 10; // 在普通代码块中声明局部变量
System.out.println("普通代码块中的变量 x:" + x);
}
System.out.println("方法内的代码块之后");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.doSomething();
}
}
方法内的代码块之前
普通代码块中的变量 x:10
方法内的代码块之后
(二)成员代码块(非静态代码块)
非静态代码块是定义在类中的代码块,没有使用 static
修饰符修饰。非静态代码块只有在创建对象实例时被执行,每次创建对象时都会执行一次。它在构造函数之前执行,用于初始化实例变量或执行其他需要在对象创建时进行的操作。程序编译时会把构造块的内容放到构造方法里的最前面执行。(非静态成员变量赋值看定义顺序,谁在后就按照谁的值执行,用的比较少)
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
Student student2 = new Student();
}
}
class Student{
String name;
{
//构造块
System.out.println("Student类的构造块");
}
public Student(){
System.out.println("Student类的无参构造");
}
}
(三)静态代码块
1、定义在类中,使用static修饰的代码块,在类加载时执行一次,与对象无关。无论产生多少对象,静态代码块只在类加载时执行一次。
2、主类中的静态代码块 > 非静态代码块 > 构造方法 > 主方法执行。(JVM要执行主方法,首先要加载主类,只要类一加载,静态块就执行了)
3、 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)。
public class MyClass {
private static int count;
static {
count = 0;
System.out.println("静态代码块被执行");
}
public MyClass() {
count++;
}
public static int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
System.out.println("Count: " + MyClass.getCount());
}
}
4、静态变量存在于方法区中,类定义时就会有初始值(在以下代码中,初始值为10),类此时在方法区中。但此时类只是定义了,还没被加载。当主方法中使用了该类时,就需要把该类从方法区加载到内存中。类加载后,静态代码块就被执行了(age= 10——>age = 100)
public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.age);
}
}
class Student{
static int age = 10;
static {
age = 100;
System.out.println("Student类的静态代码块");
}
public Student(){
System.out.println("Student类的无参构造");
}
}
八、对象的打印
在Java中,如果要打印对象的信息,可以使用对象的 toString()
方法。该方法是 Object
类中的一个方法,因此所有的类都继承了它。默认情况下,toString()
方法返回一个由类名、@
符号和对象的哈希码组成的字符串。
如果你想自定义打印对象的信息,可以在自定义类中重写 toString()
方法。在重写方法中,你可以返回希望打印的字符串格式。
以下是一个示例:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person[name=" + name + ", age=" + age + "]";
}
public static void main(String[] args) {
Person person = new Person("Alice", 25);
System.out.println(person.toString()); // 或者直接使用 System.out.println(person);
}
}
Person[name=Alice, age=25]