菜鸟学习笔记:Java基础篇3(面向对象思想、程序执行过程内存分析、面向对象重要概念)

23 篇文章 1 订阅
23 篇文章 0 订阅

Java面向对象的思想

在之前的教程中往往都会反复强调什么c语言是面向过程的语言啦,Java是面向对象的语言啦,什么面向对象和面向过程有很大区别了。。。。。。感觉总是把人给绕的一头雾水,其实从程序执行流程(这里是程序执行,不包括类加载之类的JVM内部操作)的角度来说Java和C没啥区别,同样是从main函数开始从上往下一行一行依次执行,区别在于Java可以将一部分属性和方法绑定在一起,从而组成一个对象,在主程序中直接对对象进行操作,(专业概念是以类方式组织代码,以对象方式封装数据)这种思想使得我们可以把一个种类的事务进行统一管理,让编程更加符合现实生活。

是对具体事物的抽象,具有一定相同属性和行为的事务都可以归为一类,在同一类当中又可以根据其事务的不同点进一步划分出子类。下面举例来说明:

生物类 寿命 生活环境 ... grow() reproduction() 动物类 代谢方式 ... move() 植物类 微生物类 鱼类 鸟类 哺乳类

生物类是对所有有生命事物的抽象,而生物又可以分为植物、动物和微生物,它们有生物所有的属性和行为,但又有着自己独有的特征,所以它们是生物类的子类。同样的鱼类鸟类也是动物类的子类。
对象则是某个类的具体实例,像小明这个人就属于哺乳类的一个实例,同时也是动物类、生物类的一个实例,人有自己的生活环境、有自己的寿命,也可以成长、繁殖等等生物类所定义的属性都具有,同样动物类所有的属性和行为也都具备,这也是之后要说的多态。
Java类和对象的概念大致就是如此,对象是Java的核心。下面用一个学生的例子展示Java之中的类定义方法:

class Student {   //注意类的命名习惯
	String name;   //属性
	int id;

	public void study(){	//方法
		System.out.println(name + "在学习"); //在使用String类型时"+"代表字符串拼接,与int类型不同。
	}
}	

我们在在main函数中使用这个类

public static void main(String[] args) {
	Student oStu1 = new Student();  //Student属于引用类型,驼峰命名法中一般用o代表对象
	Student oStu2 = new Student();
	oStu1.name = "小明";  //设置属性
	oStu1.id = 123;
	oStu2.name = "老王";
	oStu2.id = 456;
	oStu1.study(); //调用方法会打印小明在学习。
}

这里说明一下属性是类中定义的每个成员应有的值,对每个对象都是不同的,而方法则是每个这一类所以实例都可以进行的操作。
在对象初始化时如果不对属性进行赋值则会保持其默认值。
Java运用引用类型的变量来表示一个对象,通常对象存储在内存中,引用类型通过存储对象的地址来找到对象。

Java程序执行过程内存分析

这一块本打算将类的加载过程也融入进来一块讲解,但写着写着发现不太适合Java新手阅读,所以单独抽出来作为一篇文章,大家想了解可以看 这里
首先介绍几个概念:

当前执行线程的独占空间,以栈的数据结构出现。线程私有,它保存一个线程方法的调用状态(方法中的变量也在其中),压入栈的单位为栈帧。程序执行时main方法的栈帧先入栈,如果main方法中调用了A方法,那么在执行到A方法时它的栈帧入栈,等A方法执行完成后栈帧出栈,继续执行main方法。

是一块不连续的内存空间,JVM运行过程中最大的一块内容,被所有线程共享,主要用来存放new出来的对象以及数组。
方法区
是堆的一部分,用来存放字面量(文本字符串、八种基本类型的值、被声明为final的常量等)以及符号引用(类和方法的全限定名、字段的名称和描述符、方法的名称和描述符),它是所有的线程共享的一块区域,当方法区不能满足内存分配需求时会报出OutOfMemoryError。
对于类加载过程的说明我们继续以上面学生类为例。方便大家理解简化一下main函数

public static void main(String[] args) {
	Student oStu1 = new Student();  //Student属于引用类型,驼峰命名法中一般用o代表对象
	oStu1.name = "小明";  //设置属性
	oStu1.id = 123;
	oStu1.study(); //调用方法会打印小明在学习。
}

首先声明,程序执行时针对的是.class字节码文件,字节码文件我们看不懂,但执行的情况也是按照Java程序中一行一行来的,所以以Java程序来讲解,程序执行过程中首先main函数入栈,从第一行开始执行,当运行到Student oStu1 = new Student();时,程序会调用类加载器加载类。这时在方法区就会有类的信息(注意ststic静态代码块会在这个时候执行)。
类加载
类加载完后再堆栈中变量oStu1(oStu1就是引用变量)会被存储。
堆栈
这样Student oStu1这半句已经执行完,到后半句new Student()时,在堆的非方法区部分会生成Student类的实例,实例中的属性会被赋予属性类型的默认值,方法会指向方法区Student类存储的方法,最后引用变量oStu1会指向这一实例,整个内存结构如图所示:
整个内存结构
讲到这里大家应该了解引用变量了,其实就是在这个变量中存储的是对象的地址(实际在那个空间存储的是如十六进制的08A4这样的数,它代表对象在内存中的存储地址,程序根据地址可以找到变量并取值,这个过程图中用箭头表示),我们访问时可以通过地址直接取到对象。
然后程序继续执行,到oStu1.name = "小明"执行时,首先会根据oStu1的地址找到Student对象,然后改变对象中属性的值,oStu1.id = 123也是一个道理。
oStu1.study()首先会根据oStu1的地址找到Student对象,再根据对象的study指针找到方法区中的方法代码,将方法代码压入堆栈执行方法,执行到打印函数时System.out.println()函数入栈,栈结构如下:
栈结构
输出结束后执行完成后System.out.println()方法出栈,继续执行study()函数,发现函数执行完成,study()函数出栈,继续执行main函数中的代码,然后main函数也执行完成,这时main函数出栈,整个程序结束。

Java垃圾回收机制

上面讲到再用new关键字创建对象时,堆中会分配一块内存给对象,但如果一真建立下去内存肯定会占满,所以及时清理不用的对象释放资源非常必要,在C语言中需要我们在完成操作后手动的释放资源(比如释放链表),在Java中的垃圾回收机制可以帮我们解决这个问题,在当没有引用变量指向一个对象时,这个对象将被Java的垃圾回收器自动清除,比如在上例中我们在添加一句:

public static void main(String[] args) {
	Student oStu1 = new Student();  //Student属于引用类型,驼峰命名法中一般用o代表对象
	oStu1.name = "小明";  //设置属性
	oStu1.id = 123;
	oStu1.study(); //调用方法会打印小明在学习。
	oStu1 = null; //这时堆中的Student对象就会被垃圾回收器清理掉
}

在Java中程序员无权调用垃圾回收器,虽然可以通过System.gc()方法通知GC运行或者finalize释放资源,但一般情况都靠程序自己来处理。

构造方法

构造方法又称构造器,用于构造类的实例(也就是对象),它是一种特殊的方法,方法名称与类名相同,会在执行new语句时调用。
下面举例来说明,同样是学生类,我们加上构造方法:

class Student {   //注意类的命名习惯
	String name;   //属性
	int id;

	public Student(String name, int id){ //构造方法
		this.name = name;
		this.id = id;
	}

	public void study(){	//方法
		System.out.println(name + "在学习"); //在使用String类型时"+"代表字符串拼接,与int类型不同。
	}
}	

这时实例化对象就可以这样来:

public static void main(String[] args) {
	Student oStu1 = new Student("小明"123);  
	oStu1.study(); //调用方法会打印小明在学习。
}

大家应该明白构造方法是干嘛得了吧,但为什么之前的例子中没有这个构造方法不会报错那,因为如果在类中没有写构造方法那相当于:

//这个类的定义和最初例子中的类一致
class Student {   //注意类的命名习惯
   String name;   //属性
   int id;

   public Student(){ } //最初的例子默认自动添加

   public void study(){	//方法
   	System.out.println(name + "在学习");
   }
}	

构造方法的作用是帮助我们在对象初始化时进行一定的操作,比如对属性赋初值(static静态代码块是在类初始化时执行,先于构造方法,这个在面试中常考)。

方法重载(overload)

方法重载这个概念非常简单,但初学者比较容易和以后要讲的方法重写给搞混了,这就需要大家随用随看,多过几遍自然记住了。
方法重载就是指在一个类中可以有多个同名方法,在调用时根据参数数量不同选择不同的方法进行调用,构造方法同样可以重载,话不多说直接上例子,现在上面学生例子中需要添加学习的具体科目,所以可以在添加一个study方法,同时还需要在定义学生对象时就设置好姓名和id,所以代码可以修改成如下形式:

class Student {   //注意类的命名习惯
   String name;   //属性
   int id;

   public Student(){ }  //构造方法
   public Student(String name, int id){ //构造方法重载
   	this.name = name;
   	this.id = id;
   }

   public void study(){	//方法
   	System.out.println(name + "在学习"); 
   }
   
   public void study(String str){	//方法重载
   	System.out.println(name + "在学习" + str); 
   }
}	

然后就可以这样调用:

public static void main(String[] args) {

	Student oStu1 = new Student("小明"123);  
	oStu1.study(); //调用方法会打印小明在学习。
	oStu1.study("语文"); //调用方法会打印小明在学习语文。
	
	Student oStu2 = new Student();  //另一种构造方式
	oStu2.name = "老王";
	oStu2.id = 456;
	oStu2.study("数学"); //调用方法会打印老王在学习数学。
}

注意参数不同表示参数类型、个数或顺序不同,如果只是形参的名称不同则不构成方法重载。

public Student(String name, int id){ }
public Student( int id, String name){ }
//这是两个不同方法形成方法重载

public Student(String name, int id){ }
public Student( String name1,int id1){ }
//不构成重载会报错

public void study(String str){}
public int study(String str){}
//也不构成重载会报错

static关键字

上面例子中的变量和方法调用必须要把类进行实例化,而由一类变量或方法直接通过类名就可以调用,这就是用static关键字修饰的类名和方法。实际项目中static变量主要运用于定义方法工具栏,我们将一些共性变量和方法用static类型存储在一个类中,以便我们在程序中随时调用。
static方法修饰的变量和方法由一个特点,在类被载入时就完成了初始化,一个类型只有一份,并且它被所有类的实例所共享。在调用时用“类名.属性”来调用。还是上面的例子

class Student {   //注意类的命名习惯

  static String cType = "学生类"public static void getType(){
  	System.out.println("这是" + cType); 
  }
  
  String name;   //属性 
  int id;
  
  public void study(){	//方法
  	System.out.println(name + "在学习,"+ "我是" + cType); 
  }
}	

在调用时

public static void main(String[] args) {

	System.out.println(Student.cType); //打印学生类
	System.out.println(Student.getType()); //打印这是学生类
	Student oStu1 = new Student("小明"123);  
	oStu1.study(); //调用方法会打印小明在学习,我是学生类。表明对象可以使用static变量。

}

static变量存放在方法区中,在之前讲解类加载过程时已经说明 ,形式如下:
在这里插入图片描述
注意由于静态属性和方法在类加载完成后就完成了初始化,所以静态方法不能调用非静态属性和方法。
对static关键字还有一个点就是静态代码块static{},这里的代码会在类初始化完成后进行,它可以使用静态变量。
对于静态变量方法与普通变量方法的区别是面试的常考问题,根据上面的原理基本可以分析出来,我在这里找了一篇比较好的总结大家可以看看。
链接

this关键字

相信学过JavaScript的同学对this关键字不太陌生,也相当头疼(本人也是这么过来的,以后有机会我也会发一遍JavaScript教程,希望大家也能支持),Java中this作用与其类似但没有那么恶心。
this被称为隐式参数,当我们调用方法时即使没有定义参数,程序也会默认传入一个名为this的参数,在普通方法中使用它会指向正在调用该方法的对象。而在构造方法中调用它会指向正要初始化的对象。

class Student {   //注意类的命名习惯

  public Student(){
  	System.out.println(“构造方法被调用”); 
  }
  public Student(String name, int id){ 
  	this.name = name;//这里是this的主要用途,因为此时该函数中的name代表外边传入的name,我们只能通过this获取类中的name属性。
  	this.id = id;
  }
  String name;   //属性 
  int id;
  
  public void study(){	//方法
  	this(); //this的特殊用法,用this调用构造方法,了解即可
  	System.out.println(this.name + "在学习,"); 
  }
}	
public static void main(String[] args) {

	Student oStu1 = new Student("小明"123);  //这里调用this指向正要初始化的对象。也就是oStu1指向的对象
	
	Student oStu2 = new Student();  //另一种构造方式
	oStu2.name = "老王";
	oStu2.id = 456;
	oStu2.study(); //调用方法会构造方法被调用换行后在打印打印老王在学习,this正在调用该方法的对象也就是也就是oStu2指向的对象。
}

this关键字不能用于static修饰的静态方法,因为静态方法随类而创建,此时还没有对象,根据前面的知识很好理解这一点。

上一篇:菜鸟学习笔记:Java基础篇2(变量、运算符、流程控制语句、方法)
下一篇:菜鸟学习笔记:Java基础篇4(面向对象三大特征).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值