转-Java基础深入解析——类与对象
转自CSDN博客,u010425776,http://m.blog.csdn.net/u010425776/article/details/46932867
成员变量与局部变量的区别
1.成员变量定义在类中,整个类中都能够访问。
局部变量定义在局部代码块中,只能在局部代码块中访问。
2.成员变量存在于堆内存中,有初始值。
局部变量存在于栈内存中,没有初始值。
PS:成员变量分为静态成员变量和非静态成员变量,非静态成员变量存在堆内存中,静态成员变量存在静态方法区中。
静态方法区只存静态成员变量;
非静态方法区存类和函数的代码;
堆内存只存非静态成员变量(就是对象);
栈内存只存局部变量;
3.成员变量随着对象的创建而创建,随着对象的消失而消失(对象的消失是等对象变成垃圾之后由JVM去收回的)。
局部变量随着局部代码块的出现而出现,随着局部代码块的结束而消失。
注:类就相当于一个手机模板,对象是这个模板创造出来的一个个手机,而且每个手机(对象)中的数据都是独立存在的,即在堆内存中有各自的空间,互不影响。
匿名对象
1.定义:new Car();
2.使用场景
a) 当只需要对一个对象进行一次方法调用时使用,如:
newCar().run();
//用完就找不到这个对象了,这个对象就变成垃圾
b) 当做函数的实际参数进行传递,如:
run(new Car());
//这个对象不会变成垃圾,函数中必有一个引用类型的变量接收它
封装成员变量
1.实现方法
a) private int age;//在成员变量前加上private
b) public void setAge(int age){
if(age>=0&&age<=120)
this.age= age;
else
System.out.println(“年龄不符合要求!”);
}
public int getAge(){
return this.age;
}
2.封装成员变量的好处
对给成员变量赋值操作进行限定,在set方法中增加if语句,当输入非法数据时可以提示用户,从而提升了安全性。
对于只需要访问的成员变量就只提供get方法,对于只需要赋值的成员变量只提供set方法,从而增加了安全性。
注:1.private是权限修饰符,只能用于修饰成员变量和成员函数,不能用于修饰局部变量!
2.封装是一个范围大的概念,只要把实现细节装起来,用户只要知道如何使用就是封装。而私有是封装的一种体现,私有一定是封装,而封装不一定非要私有。比如:类、函数都是封装,这个函数、这个类可以是对外开放的,但用户无需知道其中的实现细节,只要知道怎么用就行了,这就是封装。
构造函数
1.特点
a) 构造函数的名字与类名一致
b) 构造函数没有返回值
2.作用
构造函数是用来给对象进行初始化(即创建对象的时候就给对象中的成员变量一些初始化值)
注:Person person = new Person();
new Person()其实就是调用构造函数Person()。
3.默认构造函数
在一个类中若没有定义构造函数,编译器会自动加一个默认构造函数:public Person(){}
在一个类中若有构造函数,就没有默认构造函数。
4.构造函数与一般函数的区别
a) 构造函数是在对象创建时被调用,一个对象只能调用一次构造函数,用于给对象的成员变量进行初始化的赋值。
b) 一般函数是在对象被创建之后才能被调用,可以被多次调用。
5.何时使用构造函数
若某一事物已存在就需要具备一些内容,则这些内容通过构造函数传递给成员变量。
6.构造函数的重载
与普通函数重载一致,函数名相同,形式参数个数不同or形式参数类型不同。
7.构造函数的内存分配过程
Person p2 = new Person(“小强”,10);
p2.speak();
1.执行main函数:main函数进入栈内存
2.Person p2:main的方法栈中创建引用类型的变量p2
3.new Person:在堆内存中创建Person类型的对象,并将name和age赋上初始化值null、0
4.new Person(“小强”,10):找到构造函数Person,将其压入栈内存,并创建局部变量n、a,并完成赋值:n=小强,a=10。
5.=:将构造函数Person中n、a的值赋给刚才的对象
最后将对象的首地址赋给p2,结束对象的构造。
注意:
a)函数中的局部变量进的是栈内存(构造函数也不例外),而对象中的成员变量进的是堆内存。
b)对象首先被初始化,且值都是默认值,然后再在栈内存中创建构造函数并进行赋值操作。最后将栈内存中构造函数的局部变量值赋给刚才的那个对象的成员变量。
c)栈内存中的构造函数寻找它所对应的对象是通过this指针。
8.有了带参的构造函数是否还需要set方法?
需要。构造函数只能在对象初始化的时候给对象中的成员变量进行一次赋值,创建完对象之后就不能再赋值了;若今后需要修改对象中的成员变量,就需要用set方法。
9.能否在成员函数中调用构造函数?
不行!构造函数只能在初始化对象的时候调用,其他任何情况都不能使用。
10.构造函数是否有return?
有!任何一个函数中都有return,只不过若函数没有返回值的话可以不写return,编译器自动加上“return;”。构造函数是可以写的,不过只能是return;表示中断方法而不返回值。
11.构造代码块
a) 定义:{}
b) 位置:类之中,函数之外。
c) 特点:创建对象时被执行,而且每创建一个对象被执行一次。
d) 作用:若一个类的多个构造函数中有相同的操作,那么把这些操作抽取出来放在构造代码块中,每次创建对象都会被执行,从而提高了代码的复用性。
e) 注:构造代码块是在创建对象时被执行,而且先于构造函数执行。
f) 构造函数执行次序
i. 进入构造函数
ii. 执行super,即进入父类构造函数
iii. 对本类成员变量显式初始化
iv. 执行本类构造代码块
v. 执行super之后的语句
注:局部代码块、静态代码块、构造代码块的区别是什么?
1.位置
a) 局部代码块处在函数中
b) 静态代码块、构造代码块处在类之中、函数之外。
2.生命周期不同
a) 局部代码块:函数被调用时执行,执行完自动释放内存。
b) 静态代码块:类被加载的时候执行,执行完之后自动释放内存。
c) 构造代码块:对象被创建的时候执行,类消失才消失。
3.各自特点
a) 局部代码块:在函数中只执行一次,执行完自动销毁。
b) 静态代码块:类被加载之后立即执行一次,执行完自动销毁。
c) 构造代码块:对象被创建的时候先于构造函数执行,而且执行完后仍然保存在内存中,对象被创建一次它就执行一次。
4.各自作用
a) 局部代码块:是得程序员手动控制局部变量的生命周期,从而能够节约内存。
b) 静态代码块:为全是静态成员变量的类初始化。
c) 构造代码块:把同一个类中多个构造函数中通用的操作放在其中,从而提升代码复用率。
this关键字
1.定义
this是当前函数所属对象的引用。
注:
a) this一定在函数中。
b) this是栈内存中的函数中的局部变量与堆内存中的对象的桥梁。函数中的this就代表了当前这个函数所属的对象。
2.this内存过程
Person p = new Person(“旺财”);
a) main函数全部代码存入方法区,依次执行main中代码。
b) Person p:在main方法栈中创建引用类型变量p
c) new Person:在堆内存中创建对象,且在对象中创建成员变量,并给成员变量赋上初始值null、0。
d) new Person(“旺财”):将构造函数Person所有代码压入方法区,在Person函数对应的方法栈中创建局部变量name,并同时还创建了这些局部变量所属对象的this指针。(此时,栈内存中的Person的局部变量们与堆内存中的对象便有了桥梁,连接在了一起。)
e) Person(name){this.name=name}
在Person构造函数中,将局部变量name赋给对象中的name。
f) 最后将对象的首地址赋给栈内存中的p。
g) p.speak():调用speak函数,则该函数的全部代码进入方法区,由于是对象调用函数,对象会给函数添加一个this指针,表示方法区中的这个函数属于堆内存中的对象。
3.构造函数间的调用过程
a) 在一个构造函数中调用另一个构造函数:this(参数);Person(Stringname){
this.name= name;
}
Person(Stringname,int age){
this(name);//调用第一个构造函数
this.age= age;
}
b) 内存过程
c) 构造函数的相互调用时要用到this(XXX),这句话一定得是构造函数的第一句话,否则报错。
static关键字
1.特点
a) static用于修饰类中的成员变量和成员函数,修饰完之后是被类所产生的所有对象共享。
b) 静态成员存在于类中,类存在之后,静态成员也便存在。因此,先出现类,再出现静态成员,最后出现对象。
2.静态成员的调用
类名.成员名
3.静态变量与成员变量的区别
a) 生命周期不一样。
静态变量随着类的产生而产生,随着类的消失而消失。
PS:程序一执行就把main函数所在的类加载到内存里(其他类的进入内存是在new一个对象时或类名.xxx时),因此静态变量也在这个时候被创建;JVM停止类才消失,从而静态变量才消失。
成员变量随着对象的产生而产生,随着对象的消失而消失。
b) 调用方式不一样。
成员变量只能被对象调用;
静态变量既可以被对象调用,又可以被类名调用。
c) 别名不一样。
成员变量=实例变量
静态变量=类变量
d) 存储位置不一样。
成员变量存储在堆内存中(是属于对象的特有数据);
静态变量存储在方法区(==共享数据区)的静态区(是所有对象的共享数据)
PS:纠正一个错误:堆内存只存对象(非静态成员变量),栈内存只存局部变量,而局部变量所属的函数不放在栈内存中,函数都放在方法区!方法区=共享数据区。
4.内存终极版(to be continue…)
PS:纠正:类何时被加载?主函数所属的类是在程序开始执行的时候被加载进内存,而其余的类是在执行到的时候才被加载进内存!
5.静态函数何时使用?
就看一点,只要函数中使用到了某一对象特有的数据,就只能使用普通函数了;若函数中没有使用到某一对象的特有数据,则静态函数和非静态函数都可以使用。
6.静态代码块
1.定义
static{}
2.作用
随着类的创建而执行,而且就在类被创建的时候执行一次。
用于给全是静态成员的类进行初始化值。
PS:
a)Person p = new Person()这种方式叫做:通过构造函数初始化类,并创建一个对象。
b)一个类要是全是静态成员,它没有必要通过构造函数初始化,通过静态代码块初始化即可。
主函数的解析
1.主函数与普通函数的区别
a) 主函数格式固定不变。
b) 主函数被JVM调用,而普通函数被程序员调用。
2.public
主函数要给JVM访问,因此得公有。
3.static
主函数是个静态函数,不需要创建对象就能够访问。
运行程序时我们输入命令“java 类名”,然后虚拟机直接执行“类名.main”。
4.void
主函数不需要返回值,没必要把执行结果返回给JVM。
5.String[]args
这是主函数的形式参数,是一个String类型的数组。
a) 干嘛用?
执行main函数的时候可以往里面传入一些值
b) 怎么用?
在DOS中运行程序时:
java类名参数1 参数2 参数3……
注:args是形式参数名,一个函数的形式参数名可以随便改。
javadoc的使用
javadoc -d [存放的目录] -auther-version XXX.java。
XXX.java这个类必须得是public。
只有/** */这样的注释才会被解析。
单例设计模式
1.设计模式是什么?
是解决问题的一种思想,任何语言都可以使用。
2.单例设计模式是什么?
保证一个类在整个电脑的内存中只有一个对象。
3.何时使用单例设计模式?
比如,我们可以把软件的配置信息加载到一个类中进行读写操作,此时这个配置信息的对象只能有一个,这样才能保证不同的地方读写配置信息的时候是对同一个配置信息进行操作的。
4.解决办法?
a) 不允许其他程序new该类的对象
b) 在该类创建一个本类的实例
c) 对外提供一个方法,让其他程序能通过这个方法来获取这个实例。
5.程序实现
PS:创建对象只能通过构造函数实现,如果构造函数是private,则在这个类之外就不能创建对象了。
a) 为了其他程序无法new本类对象,将本类构造函数私有化。
b) 自己在类中new一个对象。
c) 对外提供一个共有方法来提供这个对象。
class Single{
private static Single s = new Single();
private Single(){}
public static Single getInstance(){
returns;
}
}
class Test(){
public static void main(){
//获取同一个对象!
Singles1 = Single.getInstance();
Singles2 = Single.getInstance();
}
}
注:单例模式中外界只能通过公有函数getInstance()来获取对象,而访问类中的公有方法有两种途径,一种是构造对象,通过对象调用;一种是类名.函数名。由于单例模式中构造函数已经被私有化,因此无法通过new来创建对象来调用getinstance,只能通过静态调用,因此,getInstance必须是static,从而getInstance中的Single对象也必须是static。
6.内存图解
7.单例第二种实现方式(懒汉式=延迟加载形式)
class Single{
private static Single s = null;//类初始化时对象不加载
private Single(){}
public static Single getInstance(){
if(s==null)
//调用getInstance时再创建对象
returns = new Single();
}
}
第一种方式:饿汉式,类一被加载对象就被创建。
第二种方式:懒汉式,调用getinstance时对象才被创建。
注:效率其实差不多,因为不管什么时候创建对象,这个对象在内存中就只有一个,小的忽略不计,一般都选第一种。
第二种方式在多线程中会有问题!
包的概述
1.包在文件系统中就是文件夹。
2.同一个文件夹下不能有重名的文件,因此用包来区分。
3.包的定义写在类文件的第一行:
package 包名1.包名2
4.包也是一种封装的形式。
5.编译含有包的.java文件
javac -d . 类名.java
-d:编译前根据包名自动地创建文件夹。
“.”:表示当前目录,即在当前目录下根据包名创建文件夹,然后再编译.java文件。
6.运行含有包的类文件.class
java 包名1.包名2.类名
包的权限?
1.在一个包中要访问另一个包的成员函数的话,被访问成员函数和其所在的类必须是public。
注:public修饰的类的类名必须和该类所在的文件名一致。
2.被protected修饰类,能被本包的类访问,不能被其他包的类访问,但被protected修饰的类的子类在其他包中,这些子类可以访问父类。
|
public |
protected |
default |
private |
同一类中 |
OK |
OK |
OK |
OK |
同一包不同类 |
OK |
OK |
OK |
|
子类中(不论是否在同一包中) |
OK |
OK |
|
|
不同包中 |
OK |
|
|
|
包的导入?
在包顶一下写:
1.导入特定包下的特定类
import 包名1.包名2.包名3.类名;
2.导入特定包下所有的类
import 包名1.包名2.包名3.*;
注:只导入 包名1.包名2.包名3中的所有类文件,若里面还有子包则不导入。
3.一般用到哪个类导入哪个类,不用通配符导入。
把项目打包成Jar文件?
使用JDK/bin/Jar工具。
输入“jar”查看帮助。
“jar -cf 文件名.jar 需要放入jar包的包名”
为什么要把项目打包成Jar文件?
Jar包实质上给项目又多加了一层目录,而且Jar无需解压可以直接调用里面的类。
第三方工具包都是Jar包的形式,我们拿到后只要把Jar包所在的路径配置到classpath中去之后,我们就可以直接调用Jar包中内容了。