一、三种内存区域
java 的jvm 内存分为 3部分:
1:java 栈 stack :
所有的局部变量都在栈中分配空间。
静态内存分配的。每个方法调用的时候占用多少内存在编译期就确定了。
栈内存是连续分配的,因此栈中数据的存取效率略低于cpu 寄存器,效率比较高。
所以,如果在方法内部能用局部变量实现的功能,尽量使用局部变量。
栈内存的回收是方法返回自动回收。
2:java 堆 heap :
所有的new 出来的对象都在堆中分配空间。如数组,对象等等。
堆是jvm 动态管理的。在运行期对创建的对象进行空间动态的分配。
内存管理灵活,但是效率相对较低。
堆内存有专门的进行垃圾内存回收的机制(精彩内容待续...)
3:方法区method area:
运行时需要的数据存储区:不回收。
1.类的字节码元数据(模版数据)。
2.常量池
3.静态数据区
从物理内存上来讲堆和方法区都属于堆。因为存放管理的数据不同,加以名字上的区分。
二、this
this:java 一个关键字。
1:编译器在编译之后,会在所有的实例方法中(包含构造方法)访问的本类的实例成员(实例成员量、实例成员方法)的前面默认的添加this。
什么是this:this是对象(object)的引用(reference)。是当前对象的引用。
什么是当前对象?
当前被调用的方法,哪个对象调用的该方法,那么该对象就是当前对象。
this作用:
1:实现了不同的对象访问同一个实例方法,方法内部访问的是不同对象的属性。
2:使用this 区分局部和成员变量,如果局部变量和成员变量名字冲突。
3:在类的构造方法内的 第一句访问本类的其他的构造方法。
例:
package com.bjsxt.oop0522;
//自定义个描述人的类Person:属性:age,name,gender,weight,count(记录生成了几个Person 对象 只能使用现有知识)
//提供一个默认无参的,一个带参数的构造方法,构造方法内对成员变量赋值,使用this区分。
//定义一个Person数组,元素个数为 6 ,然后使用count 属性,用来统计 创建的Person对象的个数。
//
//定义方法:show 展示自己的属性。定义方法 eat 修改自己的属性weight 。
//将数组中所有的对象依次调用 eat 和 show 方法。
//
//最后,通过每个对象的 count 属性,都可以得到创建了几个Person对象。
public class TestStatic {
public static void main(String[] args) {
final int COUNT = 6;
Person[] persons = new Person[COUNT];
persons[0] = new Person(12, "小花", "女", 99);
persons[1] = new Person(13, "小刚", "男", 60);
persons[2] = new Person(16, "小兰", "女", 50);
persons[3] = new Person(17, "小明", "男", 100);
persons[4] = new Person(19, "韩梅梅", "女", 110);
persons[5] = new Person(20, "翠花", "女", 130);
for (Person person : persons) {
person.eat();
person.show();
}
}
}
class Person{
int age;
String name;
String gender;
int weight;
static int count;
public Person() {
}
public Person(int age, String name, String gender, int weight) {
this.age = age;
this.name = name;
this.gender = gender;
this.weight = weight;
count ++;
}
void eat(){
System.out.println("人多吃蔬菜水果,对身体好!");
weight ++;
}
void show(){
System.out.println("name = "+name + "\tage = "+age + "\tgender = "+gender + "\tweight = "+weight + "\tcount = "+count);
}
}
三、Static
static:是java 的一个关键字,静态的意思。
static 可以用来修饰类的成员变量,还可以修饰成员方法(有限制的修饰,并不是所有的都需要,或者是都可以)。
static 修饰成员变量:内存分配的时机:
当在代码中访问某个类的时候,第一步要对类进行类加载的判断,如果没有加载,那么先加载类的字节码到方法区。
类加载:装载(将class文件字节码信息装载到内存)--->链接--->类的初始化(对类的静态成员变量进行空间的分配(第一步对静态变量进行空间的分配并执行默认初始化,再进行声明处的赋值按照从上到下的顺序))
特点:
1:类加载只有一次,所以静态的成员变量内存只分配一次。只有一份内存空间(方法区)。
2:静态成员变量的内存不依赖于任何的对象。依赖于所在的类。(类变量--区分--实例变量)
3:生命周期依赖于加载的类。java 程序结束了回收。
4:所有的该类的对象都可以访问 static 变量的内存空间。被所有的对象共享的。可以通过对象引用访问静态成员变量。
5:除了可以通过对象访问静态成员变量,还可以通过类名直接访问静态成员变量。两种访问机制(对象.静态变量名 或 类.静态变量名)
--什么样的成员变量可以使用static 修饰?
1.不可变的:static final int MAX_AGE = 135;
2.需要优先赋值的。
static 修饰成员方法:
静态成员方法特点:
1:类加载完毕之后就可以使用了。不需要创建对象。
2:可以使用对象调用静态方法,也可以使用类名调用方法。(本类访问静态成员,通常类名省略)
作用:
1:可以用来访问静态成员变量(只能是局部 或静态成员)。(通过静态的方法访问静态成员变量)
2:可以用来作为工具方法。(Math 数学工具类不需要创建对象直接用类名来访问静态方法 Arrays)。
注意:main 方法 是静态的。可以保证 包含入口的类一旦加载,就可以执行main 方法,而不需要创建所在类的对象。
思考:
1:静态方法是否可以访问类的实例变量?
不可以:静态方法可以调用的时候,不一定创建了对象,实例变量的内存是依赖于对象的。
2:静态方法中,是否可以有this?
不可以:同上
3:类的实例方法中是否可以访问静态成员?
可以,对象已经生成,那么类加载早已经完成,静态成员的内存空间已经分配了,可以安全访问。
四、代码块
类中包含的内容:
成员变量(实例成员变量、静态的)、成员方法(实例的,静态的)、构造方法。
1:局部代码块:在方法中的一对大括号。
作用:可以将局部变量的作用域范围控制的更小。
2:构造块:在类的内部,方法的外部的一对大括号。
特点:每生成一个该类的对象,构造块就会被执行一次。先于构造方法执行。
作用:对对象属性进行初始化的。
3:静态块:在类的内部,方法的外部static{}
特点:在类加载的时候,类初始化的那一步static 代码块被执行,且仅执行一次。
作用:可以在静态块中对静态的成员变量进行初始化。对一些只执行一次,而且希望优先执行的功能,可以放在静态块中执行。
注意:静态块中不能访问实例成员。
public class TestBlock {
public static void main(String[] args) {
//局部代码块
{
int age = 10;
System.out.println(age);
}
Tree tree = new Tree();
new Tree();
}
}
class Tree{
int age = 10;
//构造块
{
System.out.println("Tree.enclosing_method()");
}
//静态代码块
static{
System.out.println(" - -static - - Tree.enclosing_method()");
}
public Tree() {
System.out.println("Tree.Tree()");
initTree();
}
void initTree(){
age = 100;
}
}
五、生成一个对象的过程:
1:类加载,在类加载的最后,类初始化部分,对 静态成员变量进行初始化,并执行静态代码块。
2:由jvm 在堆中分配一块内存供对象使用,并对对象的所有的属性进行默认初始化。
3:按照从上到下的顺序执行成员变量声明处的赋值以及构造块中的代码。
4:执行构造方法的代码。
5:构造方法执行完毕,对象初始化完毕。由new 关键字将生成对象的内存地址返回 。
六、封装
面向对象的三大特性:封装encapsulation、继承 inheritance、多态 ploymofisim。
程序是用来处理数据的。
java 程序的基本的组成单元就是类。
java 处理的程序的数据就是类中的属性,成员变量。
封装:希望被别的对象访问的数据和功能对外提供接口,不希望被外部访问的部分隐藏起来。这个过程称为封装。
通过什么样的方式来实现对数据和功能的隐藏和暴露?
如何控制类成员的被访问的权限?
java 中提供了3个 四类 权限访问修饰符。来实现对类成员的访问权限的控制。
java 中的类成员的访问权限修饰符:
public:公有的意思。被访问的最大的权限。
工程级别的:
本类访问、本包其他的类、跨包子类、跨包的其他的类。
protected:受保护的:
访问权限:
本类、本包其他的类、跨包子类。
默认的:
访问权限:包私有权限
本类、本包其他的类。
private :私有的。
访问权限:本类访问。
类成员的访问权限的一些使用的基本规律:
1:所有的成员变量全部私有化。如果需要其他的类访问私有的成员,通过统一的对外的修改器和访问器实现 accessor。 getter and setter。
2:类的所有的成员的访问权限要尽量的小。
类的访问权限:只有两个:
public:本工程的所有的类都可以访问public 修饰的类。
默认的:本包的其他的类可以访问默认权限的类。
例:
/**
* 封装
* @author yhl
* 定义一个Student类,属性,age,name,score,gender,
所有属性全部私有化。
然后对所有的成员变量,提供 访问器和修改器 访问器 getter,public ,修改器 setter 访问权限 default。
定义构造方法,通过参数设置属性的值(不要直接对属性赋值,而是通过方法赋值)!
定义show 方法,展示属性。
定义无参的构造方法。
定义测试类:
生成Student对象,通过有参的构造方法,然后,调用show 方法。
再生成一个Student对象,调用无参的构造方法。通过修改器对所有的属性赋值。然后调用show 方法。
*
*/
public class TestEncapsulation {
public static void main(String[] args) {
Student student = new Student(12,"小花",99,"女");
student.show();
Student student2 = new Student();
//链式
student2.setAge(17).setName("小刚").setGender("男").setScore(100).show();
}
}
class Student{
private int age;
private String name;
private int score;
private String gender;
public Student() {
}
public Student(int age, String name, int score, String gender) {
setAge(age);
setName(name);
setScore(score);
setGender(gender);
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
public String getGender() {
return gender;
}
Student setAge(int age) {
this.age = age;
return this;
}
Student setName(String name) {
this.name = name;
return this;
}
Student setScore(int score) {
this.score = score;
return this;
}
Student setGender(String gender) {
this.gender = gender;
return this;
}
public void show() {
System.out.println("Student [age=" + age + ", name=" + name + ", score=" + score + ", gender=" + gender + "]");
}
}