理解Java中的对象,变量和方法
1.对象的创建和销毁
1.1 对象的创建
这里只介绍创建对象与构造方法的关系
(1).每实例化一个对象就会自动调用一次构造方法,实质上这个过程就是创建对象的过程,准确的说,在Java语言中使用new操作符调用构造方法创建对象。
(2).当创建对象时,自动调用构造方法,也就是说在Java语言中,初始化与创建对象是捆绑在一起的。
(3).每个对象都是相互独立的,在内存中占独立的内存地址,并且每个对象都有自己的生命周期,当一个对象的生命周期结束时,对象变成垃圾,由Java虚拟机回收处理。
1.2 对象的引用
引用只是存放一个对象的内存地址,并非存放一个对象,严格的说引用和对象是不同的,但可以忽略。
1.3 对象的比较
在Java语言中对象的比较有两种方式,分别为 “==” 和 “equals()”,但这两种方式有本质的区别:
例:
- public class Compare{
- public static void main(){
- String c1 = new String("acb");
- String c2 = new String("acb");
- String c3 = c1;
- System.ou.println(c2==c3);
- System.ou.println(c2.equals(c3));
- }
- }
运行结果:false
true
分析:equals()方法是String类中的方法,它可以用于比较对象引用所指的内容是否相等,而“==”运算符比较的是两个对象引用的地址是否相等,由c1与c2是两个不同地对象的引用,两者内存位置不同,而String中c3=c1,语句将c1的引用赋给c3,所以c1与c3这两个对象引用是相等的
1.4 对象的销毁
有以下两种情况会被Java虚拟机销毁:
(1).对象引用超过其作用范围
(2).将对象赋值为空
注:垃圾回收器只能回收那些由new操作符创建的对象,否则,不能回收,所以在Java中提供了一个finalize()方法,这个方法是Object类的方法,它被声明为protected,用户可以在自己的类中定义这个方法,在垃圾回收时首先会调用这个方法,在下一次垃圾回收动作发生时,才能真正回收对象占用的内存。但垃圾回收方法finaliza()不保证一定会发生。
2.访问对象属性和方法
举个例子:
- public class Test {
- int i = 47;
- public void call(){
- for (i = 0; i < 3; i++){
- System.out.print(i+" ");
- if(i == 2){
- System.out.println("\n");
- }
- }
- }
- public Test(){
- }
- public static void main(String[] args) {
- Test t1 = new Test();
- Test t2 = new Test();
- t2.i = 60;
- System.out.println(+t1.i);
- t1.call();
- System.out.println(+t2.i);
- t2.call();
- }
- }
运行结果:
- 47
- 0 1 2
- 60
- 0 1 2
分析:
在上述代码的主方法中首先实例化一个对象,然后使用“.”操作符调用成员变量和成员方法,但是从运行结果可以看到,虽然使用两个对象调用同一个成员变量,结果却不同,因为在打印这个成员变量值之前将该值重新赋值为60,单在赋值使用时的是第二个对象t2对象调用成员变量,所以在第一个对象t1调用成员变量打印该值时仍然是成员变量的初始值。由此可见,两个对象的产生是相互独立的,改变了t2的值,不会影响到t1的 i 的值。在内存中如下图所示:
如果希望成员变量不被其中任何一个对象改变,可以使用static关键字,代码如下:
- public class Test {
- static int i = 47;
- public void call(){
- for (i = 0; i < 3; i++){
- System.out.print(i+" ");
- if(i == 2){
- System.out.println("\n");
- }
- }
- }
- public Test(){
- }
- public static void main(String[] args) {
- Test t1 = new Test();
- Test t2 = new Test();
- t2.i = 60;
- System.out.println(+t1.i);
- t1.call();
- System.out.println(+t2.i);
- t2.call();
- }
- }
运行结果:
- 60
- 0 1 2
- 3
- 0 1 2
分析:
从上述运行结果中可以看到,由于使用t2.i=60语句改变了静态成员变量的值,使用对象t1调用成员变量的值也是60,这正是i值被定义为静态成员变量的效果,即使使用两个对象对同一个静态成员变量进行操作,依然可以改变静态成员变量的值,因为在内存条中两个对象指向同一块内存区域,t1.i++语句执行后,i的值变为3,当再次调用call()方法时又被重新赋值为0,做循环打印操作。
3.final变量,方法和类
(1)filnal修饰的方法不能被重写;
(2)final修饰在属性上,属性的值不能被改变。
(3)final修饰的类不能被继承。
3.1 finla变量关键字
finla变量关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值,通常,有final定义的变量为常量。
final关键字定义的变量必须在声明时对其进行赋值定义,final除了可以修饰基本数据类型的常量,还可以修饰对象引用,由于数组也可以被看成一个对象的引用,所以final可以修饰数组,一旦一个对象引用被修饰成final后,它只能恒定指向一个对象,无法将其改变指定另一个对象,一个既是static又是final的字段只占据一段不能改变的存储空间,以下面的例子深入了解final:
例:
- import static java.lang.System.*;
- import java.util.*;
- class Test {
- int i = 0;
- }
- public class FinalData {
- static Random rand = new Random();
- private final int VALUE_1 = 9; // 声明一个final常量
- private static final int VALUE_2 = 10; // 声明一个final、static常量
- private final Test test = new Test(); // 声明一个final引用
- private Test test2 = new Test(); // 声明一个不是final的引用
- private final int[] a = { 1, 2, 3, 4, 5, 6 }; // 声明一个定义为final的数组
- private final int i4 = rand.nextInt(20); //随机数
- private static final int i5 = rand.nextInt(20);
- public String toString() {
- return i4 + " " + i5 + " ";
- }
- public static void main(String[] args) {
- FinalData data = new FinalData();
- // data.test=new Test();
- //可以对指定为final的引用中的成员变量赋值
- //但不能将定义为final的引用指向其他引用
- // data.value2++;
- //不能改变定义为final的常量值
- data.test2 = new Test(); // 可以将没有定义为final的引用指向其他引用
- for (int i = 0; i < data.a.length; i++) {
- // a[i]=9;
- // //不能对定义为final的数组赋值
- }
- out.println(data);
- out.println("data2");
- out.println(new FinalData());
- // out.println(data);
- }
- }
运行结果
- 8 3
- data2
- 6 3
分析:
在本实例子中,被定义成final的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是Java中的编码规则,同时,定义为final的数据无论是常量,对象,还是数组,在主函数中都不可以被修改。一个被定义为final的对象引用只能指向唯一一个对象,不可以将它再指向其他对象,但是一个对象本身的值却是可以改变的,那么为了使一个常量真正做到不可以更改,可以将常量声明为staticfinal。为了验证这个理论,看以下实例子:
- import static java.lang.System.*;
- import java.util.*;
- public class FinalStaticData {
- private static Random rand = new Random(); // 实例化一个Random类对象
- // 随机产生0~10之间的随机数赋予定义为final的a1
- private final int a1 = rand.nextInt(10);
- // 随机产生0~10之间的随机数赋予定义为static final的a2
- private static final int a2 = rand.nextInt(10);
- public static void main(String[] args) {
- FinalStaticData fdata = new FinalStaticData(); // 实例化一个对象
- // 调用定义为final的a1
- out.println("重新实例化对象调用a1的值:" + fdata.a1);
- // 调用定义为static final的a2
- out.println("重新实例化对象调用a1的值:" + fdata.a2);
- // 实例化另外一个对象
- FinalStaticData fdata2 = new FinalStaticData();
- out.println("重新实例化对象调用a1的值:" + fdata2.a1);
- out.println("重新实例化对象调用a2的值:" + fdata2.a2);
- }
- }
运行结果:
- 重新实例化对象调用a1的值:4
- 重新实例化对象调用a1的值:1
- 重新实例化对象调用a1的值:5
- 重新实例化对象调用a2的值:1
从实例的结果可以看出,定义为final的常量是恒定不变的,将随机数赋值定义为final的常量,可以做到每次运行程序时改变a1的值,但a1与a2不同,由于他被声明为static final的形式,所以在内存中为a2开辟了一个恒定不变的区域,当再次实例化一个finalstaticdata对象时,仍然指向a2这块内存区域,所以a2的值保持不变,a2是在装载时被初始化,而不是每次创建新对象时度被初始化,而a1会在重新实例化对象时被更改。
3.2 final方法
首先说明一点,f定义为inal的的方法不能被重写。
例:
- class Parents {
- private final void doit() {
- System.out.println("父类.doit()");
- }
- final void doit2() {
- System.out.println("父类.doit2()");
- }
- public void doit3() {
- System.out.println("父类.doit3()");
- }
- }
- class Sub extends Parents {
- public final void doit() { // 在子类中定义一个doit()方法
- System.out.println("子类.doit()");
- }
- // final void doit2(){ //final方法不能覆盖
- // System.out.println("子类.doit2()");
- // }
- public void doit3() {
- System.out.println("子类.doit3()");
- }
- }
- public class FinalMethod {
- public static void main(String[] args) {
- Sub s = new Sub(); // 实例化
- s.doit(); // 调用doit()方法
- Parents p = s; // 执行向上转型操作
- // p.doit(); //不能调用private方法
- p.doit2();
- p.doit3();
- }
- }
运行结果
- 子类.doit()
- 父类.doit2()
- 子类.doit3()
分析:
从上例子中可以看出,final方法不能被覆盖。例如doit2()方法不能再子类中被重写,但是在父类中定义了一个private final的doit()方法,同时在子类中也定义了一个doit()方法,从表面上看,子类中的doit()方法覆盖了父类的doit()方法,但必须满足一个对象向上转型为它的基本类型并调用相同方法这样一个条件,在例子中,对象p不能调用doit()方法,可见,子类中的doit()方法并不是正常覆盖,而是生成一个新的方法。
3.3 final类
在这只说明一点:如果将某个类设置为final类,则类中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或非final形式,并且,其值可以被改变。
4.静态变量,常量和方法
(1)为什么要使用静态变量,常量和方法
通常,在处理问题,会遇到多个不同的类要使用同一变量,常量或方法,然而,同一个常量在不同i的类中创建时系统都会为之分配内存,造成内存浪费,如果能将这些不同类中的变量共享到一个内存中,那就大大减少了内存的使用,而静态变量(关键字 static)就是解决这个问题的。如下图所示:
(2).被声明的static的变量,常量和方法被称为静态成员,静态成员属于类所有,区别于个别对象,可以在本类或其他类使用类名“.”运算符调用静态成员。如下代码:
- public class AnyThing {
- static double PI = 3.1415; //在类中定义静态常量
- static int id; //在类中定义静态变量
- public static void method1(){ //在类中定义静态方法
- }
- public void method2(){
- System.out.println(AnyThing.PI); //调用静态常量
- System.out.println(AnyThing.id); //调用静态变量
- AnyThing.method1(); //调用静态方法
- }
- }
注意:
(1) 虽然静态成员可以使用“对象.静态成员”的形式进行调用,但通常不这么使用,这样容易混淆静态成员和非静态成员。
(2) 在静态方法中不可以使用this关键字。
(3) 在静态方法中不可以直接调用非静态方法。
(4) 在Java中规定不能将方法中的局部变量声明为static的。
(5) 如果在执行类时,希望先执行类的初始化动作,可以使用static定义一个静态区域,例如
- public class example{
- static{
- ...
- }
- }
当这段代码被执行时,首先执行static块中的程序,并且只会执行一次,即在类加载的时候就被执行,之后执行main()方法。并且只是自上而下的顺序执行。
(3)实例语句块在构造方法调用之前调用,调用一次构造函数,就调用一次实例语句块,执行顺序自上而下。
(4)在java中,静态方法和普通方法的区别:
5.this关键字
在下面代码中。成员变量与setName()方法中的形式参数的名称相同,都为name,那么该如何在类中区分使用的是哪一个比阿娘呢?在Java语言中规定使用this关键字来代表本类对象的引用,this关键字被隐士的用于引用对象的成员变量和方法,如在上述代码中,this.name指的name是成员变量,第二个name是形参。
- private void setName(String name){
- this.name = name;
- }
那么,在java中this和对象都可以调用成员变量或成员方法,二者之间有什么区别呢?
区别:this引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,就要添加this关键字说明引用的是类成员还是局部变量或方法参数;如果this关键字直接写成name = name;成员变量的值并没有改变,因为参数name在方法的作用域中覆盖了成员了变量name。
this关键字还可以调用类中的构造方法,如下代码
- public class AnyThing {
- public AnyThing(){
- this(""); //使用this调用有参构造方法
- System.out.println("无参构造方法");
- }
- public AnyThing(String name){
- System.out.println("有参构造方法");
- }
- }
在上例定义了两个构造方法,在无参构造方法中使用this关键字调用有参的构造方法,但使用这种方法需要注意的是只可以在无参构造方法中的第一句使用。
理解Java中的对象,变量和方法的更多相关文章
- java中初始化对象变量的方法
1.在类定义对象的地方初始化 2.在类构造器中初始化 3.在正要使用这些对象之前,惰性初始化,或者叫惰性载入 4.使用实例初始化 在方法里使用初始化
- 深入理解Java中配置环境变量
深入理解Java中配置环境变量 配置的目的: 本来只在安装JDK的bin目下能运行java.exe,javac.exe,jar.exe,javadoc.exe等Java开发工具包命令,我们现在想让在所 ...
- java中的中文变量和方法
在网上看到java居然支持中文变量名.方法.这里我只试了变量名和方法,类名这些没有试....真是给力 package com.gxf.fun; public class TestForChinese ...
- java中String对象的split方法
在java.lang包中有String.split()方法,返回是一个String[]数组,今天碰到一个自己没注意的问题: 1.特殊分隔符 String str1 = "123|456|78 ...
- 深入理解Java中的不可变对象
深入理解Java中的不可变对象 不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象.包装器对象等,那么到底为何Java语言要这么设计,真 ...
- 理解Java中对象基础Object类
一.Object简述 源码注释:Object类是所有类层级关系的Root节点,作为所有类的超类,包括数组也实现了该类的方法,注意这里说的很明确,指类层面. 所以在Java中有一句常说的话,一切皆对象, ...
- 彻底理解Java中的hashcode方法(转)
本文转自http://www.importnew.com/18851.html 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有 ...
- JAVA中JavaBean对象之间属性拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...
- JAVA中JavaBean对象之间拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...
随机推荐
- Nginx、MySQL、PHP 编译安装
RHEL 7.0 编译安装Nginx1.6.0+MySQL5.6.19+PHP5.5.14运行环境 准备篇: RHEL 7.0系统安装配置图解教程 http://www.jb51.net/os/192 ...
- POJ 1149 网络流 合并建图
这个题目我敲了一个简单的EK,这不是难点 难点在于建图,按题目的要求 每个猪圈和顾客都建点的话,那也太多了...我看了Edelweiss里面的缩点方法才建好的图,哎,惭愧啊 实际那些猪圈根本不需要单独 ...
- 记校赛水题----AK爷兼职计
Description AK爷最近收到一份兼职,是去幼儿园看小朋友,AK爷认为看孩子这件事情很简单,但是事实并非如此.幼儿园里的孩子们喜欢数学,不仅九九乘法口诀倒背如流而且精通各种算法.某天,AK爷上 ...
- phpstorm 的下载、安装与激活
1.phpstorm的下载地址 https://www.jetbrains.com/phpstorm/ 下载后的安装包如图: 2.phpstorm的安装过程 跟据电脑系统下载安装对应版本 一路点击下一 ...
- hook鼠标
library dllMouse; uses SysUtils, Classes, UnitHookDLL in 'UnitHookDLL.pas', UnitHookConst in 'UnitHo ...
- LeetCode——221. 最大正方形
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积. 示例: 输入: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 输出: 4 暴力法 ...
- Git 报错:fatal: refusing to merge unrelated histories
背景:[接上篇git push 出错的随笔]当 pull 远端仓库到本地的时候,出现以下错误: 错误情况: 出错原因:主要原因还是在于本地仓库和远程仓库实际上是独立的两个仓库,假如我之前是直接以 cl ...
- postman批量接口测试注意事项
1.使用cvs文件 导入文件后最后行出现\r符号 用文本打开 删除最后一行空白行 2.打印cvs文件中的接口调用的参数 Pre-request Script: var beginDate=data.b ...
- lemon
这本是一个技术博客网站 我却用来记录关于六月 就好像特别才不配你的特别一样 就好像以后要特别喜欢cos一样 就好像再特别的六月也会过渡到七月一样
- socket实践编程1
1.服务器端程序编写 (1).socket (2).bind (3).listen (4).accept,返回值是一个fd,accept正确返回就表示我们已经和前来连接我的客户端之间建立了一个TCP连 ...