类与对象
什么是java类
类是构建对象的模板:一个类可以创建多个对象,每个对象的数据的最初来源来自对象,类定义了对象的结构。
创建一个类通常涉及以下步骤:
- 声明类:使用
class
关键字开始,后跟类名和类体。- 编写字段:在类体中声明变量,这些变量代表了对象的状态。
- 编写方法:定义对象的行为,即对象可以执行的操作。
示例
public class demo {
String name;
int num;
// 方法
public void run() {
System.out.println("跑了"+num+"米");
}
}
使用类
创建类之后,你可以通过创建该类的对象来使用它。这涉及到使用new
关键字和类的构造器来实例化对象。
new
关键字,开辟内存空间创建对象的实例。
构造器(构造方法)
作用:
在创建对象的时候为对象分配内存并给对象的变量赋初始值。
特点:
- 在类当中构造函数必须和类名相同。
- 在类当中有一个默认的不显示的无参数的构造器,一旦你在类当中写一个构造器,那么那个无参构造器就会被覆盖。
- 构造器不需要写返回类型。
- 一个类当中有多个构造方法。
示例
public class Person {
String name;
int age;
// 构造函数
public Person(String name, int age) {
this.name = name; // 使用this关键字来区分成员变量和参数
this.age = age;
}
// 另一个构造函数,没有参数,提供默认初始化
public Person() {
this.name = "Unknown";
this.age = 0;
}
// 一个方法来显示Person对象的信息
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
public class TestPerson {
public static void main(String[] args) {
// 使用带参数的构造函数创建Person对象
Person person1 = new Person("Alice", 30);
person1.displayInfo(); // 输出: Name: Alice, Age: 30
// 使用无参数的构造函数创建Person对象
Person person2 = new Person();
person2.displayInfo(); // 输出: Name: Unknown, Age: 0
}
}
什么是java对象?
对象:是类的实例。它根据类的定义被创建,并且包含类的所有属性和方法的具体值或实现。
static关键字
static是什么?
1.在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
2.static修饰符修饰的代码属于类,属于类的东西时被对象所共享的
3.没有static修饰符修饰的代码属于对象
4.静态方法可以调用静态方法,不能调用非静态方法,非静态方法也可以调用静态方法
5.static修饰的代码块在main()方法之前执行,其目的是优化程序
java为什么设计static关键字
因为要方便类去使用自己的方法和变量
如果方法和变量属于非静态的,他们只有在对象当中才会被分配内存空间,那么也就是只能有对象才能调用!
如果方法和变量属于静态的,方法和变量就会被存储在方法区的静态常量池中,并且能够直接被类所调用。
也能被对象所调用,因为对象来源于类,类是对象的模板,对象可以使用类的信息,但是不推荐
static关键字怎么使用
如果一个方法想要被main方法调用
1.在非静态方法上加入static关键字
2.在main方法当中创建对象,让对象调用该非静态方法
下面这个代码当中run()方法和fly()方法都属于对象
public class Person {
private int age;
private String name;
private char sex;
public void run(){
System.out.println("人走路");
}
public void fly(){
System.out.println("人不会飞");
}
public static void main(String[] args) {
}
}
run()方法和fly()方法都在类当中,怎么还会是对象呢,其实它属于类,但严格意义上来讲它是对象所拥有的。
举个例子,建两个文件
person文件
public class person {
public String name = "张三";
public static String address = "保定" ;
public static void run(String num){
System.out.println("num:"+num);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
"address='" + address + '\'' +
'}';
}
}
test文件
public class test {
public static void main(String[] args) {
person p1 = new person();
person p2 = new person();
p1.name = "李四";
p1.address = "北京";
p2.name = "王五";
p2.address = "上海";
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
输出的结果为:
Student{name='李四'address='上海'}
Student{name='王五'address='上海'}
为什么两次的address值全为上海,而不是北京和上海
可以根据图形进行理解
.java文件被javac编译为.class文件和类信息一同储存在方法区
方法区内有静态常量池用来储存被static修饰的代码
当代码按顺序执行到
p1.address = "北京";
静态常量池内被修改为 address=北京
当代码按顺序执行到
p2.address = "上海";
静态常量池内被修改为 address=上海
如果是一个静态方法呢,结果是否也像上面代码结果一样类似
public class person {
public String name = "张三";
public static String address = "保定" ;
public static void run(String num){
System.out.println("num:"+num);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
"address='" + address + '\'' +
'}';
}
}
加入一个静态的run()方法
test测试代码
public class test {
public static void main(String[] args) {
person p1 = new person();
person p2 = new person();
p1.name = "李四";
p1.address = "北京";
p1.run("100");
p2.name = "王五";
p2.address = "上海";
p2.run("200");
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}
执行的结果为
num:100
num:200
Student{name='李四'address='上海'}
Student{name='王五'address='上海'}
跟我们料想的不一致
用图形来理解
静态常量池内存储的是run()方法
当执行到p1.run()时调用run()方法进栈,提交100,然后出栈
当执行到p2.run()时调用run()方法进栈,提交200,然后出栈
类当中的方法是被对象所共享的
判断以下示例代码当中的输出结果
示例1
public class A {
private static int a;
public static void main(String[] args) {
modify(a);
System.out.println(a);
}
public static void modify(int a){
a++;
}
}
输出结果为
0
java只进行值的传递,modify进栈,从静态常量池中获取0,并赋给局部变量,然后局部变量自加,最后出栈,输出的结果为静态常量池中a的值
示例2
public class Demo {
public Demo(String aa){
System.out.println("==="+aa);
}
static {
System.out.println("11");
}
public static Demo demo = new Demo("+++");
static {
System.out.println("22");
}
}
class Test{
public static void main(String[] args) {
Demo demo = new Demo("---");
}
}
输出结果为
11
===+++
22
===---
代码流程为
扫描------>加载(编译)-------->运行
- main方法在哪里我们就先去扫描哪个类------>首先去扫描Test类,发现没有父类,也没有static修饰的代码,那么编译Test类,并且main方法入栈
- 执行new Demo("---");在执行之前扫描Demo类,发现没有父类,所以编译Demo类,在编译的过程中static代码块按顺序优先执行
- 首先输出的是11
- 执行 public static Demo demo = new Demo("+++");此时会涉及到调用Demo构造方法,所以输出 ===+++
- 输出22
- 编译完毕,回到main方法当中继续执行new Demo("---");现在可以创建对象了!!也就是进行到了运行阶段
- 通过调用构造器创建对象:===---