面向对象说到底就是就是一种思想,相同的C语言的面向过程也是一种思想。这里我们假设要编写一个学生信息管理系统,如果用面向过程的思想来说就是思考“怎么去实现这个程序”,于是就想出了一些步骤:
1.输入密码
2.修改学生信息
3.读取学生信息
4.······
而如果是面向对象的思想,首先思考的就应该是“怎么设计这个系统”,然后就知道了这个系统主要的就是学生的信息,而学生的信息就由如下对象组成:
1.姓名
2.年龄
3.性别
4.学号
5.······
面向对象可以帮助人们从宏观上把握,从整体上分析整个系统。-----《实战Java程序设计》
但是在面向对象的同时,在具体的实现过程中还是需要面向过程的思想,这是不可被忽视的。面向对象和面向过程(方法的实现)他们是相辅相成的。
Ps.也不知道上面的例子怎么样吧,不知道是说的正确还是不对,若有不对的地方,还请批评指正!
封装性、继承性和多态性。这三个特征将会在后学学习中掌握。
- 对象也是一种数据结构(对数据的管理模式),是将数据和数据行为放到了一起
- 在内存上,对象就是一个内存块,存放了相关的数据集合
- 对象的本质就是一种数据的组织方式
----来自《实战Java程序设计》
跟着上面的学生例子来,学生很明显就是我们要研究的对象,更具体一点,我们来研究中学生,那么什么样的人是中学生呢?那么在这个总结归纳的过程中我们就可以找到一些特征
- 穿校服
- 要参加中、高考
- 在中学里(当然在学校里还有教职工)
- ······等等
好了那么我们就抽象出来了中学生的特征,那么进一步总结归纳的话我们就可以得出一个中学生类了,类是对对象的抽象。
对象–Object/instance,类–class 类的对象==类的实例。
对象是我们要具体研究的事物,而类是对对象的抽象
类的定义方式
1.
class 类名{
属性
方法
}
2.
public class 类名{
//这个类名和源文件的名称要一样,每个源文件必须有且只有一个public class
class 类名{
...
}
...
}
用于定义类或类对象包含的数据。例如一个学生类属性包括姓名,年龄,性别等。
[修饰符] 属性类型 属性名 = [默认值];
话不多说直接上例子更加直观,以下是一个关于学生类的定义和对象的使用:
class School{
String schoolname;
}
public class Student {
String name;
int age;
String sex;
School sch;
void tell(){
System.out.println("我是"+name+",我"+age+"岁,我来自"+sch.schoolname);
}
Student(){
}
public static void main(String[] args) {
Student stu = new Student();
stu.name = "杨某人";
stu.age = 19;
stu.sex = "男";
School sc = new School();
sc.schoolname = "hnust";
stu.sch = sc;
stu.tell();
}
}
public class School {
String schoolname;
}
Student.java中删去School类就可以了。
结果如下
UML图如下
这里有些概念是新的,具体的在后学的学习中会了解到,这里弄一个这个是为了配合书籍去加深理解!
JVM的内存分为三个区域:栈(stack)、堆(heap)、方法区(method area)。
栈:线程私有,不被共享;每个方法被调用都会创建一个栈帧;“先进后出,后进先出”;内存空间连续。
堆:线程共享;存储创建好的对象和数组;JVM只有一个堆;内存空间不连续。
方法区:线程共享;只有一个方法区;本质上也是堆;存放存储类、常量信息(存放不变和唯一的内容)。
以上面的代码为例:
1.Student stu = new Student();
2.到School sc = new School();前
3.后续
因为刚学不久,所以肯定不会很熟练,这东西先放在这以后在学到相关的东西的时候可以回来看看。
7.3 还有个tell方法,应该是入栈后出栈输出了吧,emmmm…
上面的实例代码中有个Student(){}是什么呢?其实就是个构造器。构造器也叫做构造方法(是一种特殊方法),用于对象的初始化。构造器名应与类名一样。Java通过new关键字来调用构造器,返回该类的对象。
1.分配对象空间,将对象成员变量初始化为0或者空(null);
2.执行属性值得显式初始化;
3.执行构造器;
4.返回对象的地址给相关的变量。
构造器声明如下:
[修饰符] 类名(形参列表){
·····
}
Ps.若是没有定义构造器,则编译器会自动定义一个没有参数的构造函数。
构造器是一种特殊的方法,与普通的方法一样,他也可以重载。
public class Student {
String name;
int age;
String sex;
void tell(){
System.out.println("姓名:"+name+" 年龄:"+age+"岁 性别:"+sex);
}
Student(){
}
Student(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "小明";
stu1.age = 20;
stu1.sex = "男";
stu1.tell();
Student stu2 = new Student("小红", 18, "女");
stu2.tell();
}
}
Ps.上面代码的关键字this是用于区分属性和形参,this.name是属性,而n是形参
输出如下
C/C++的内存管理是很令人头疼的,而Java引入了垃圾回收机制,使得内存管理的问题迎刃而解。
在这里其实可以回去看看内存分析的部分,我们在写Java的时候会创建大量的对象,然后由于堆得内存是有限的,若是对象一直增加,那么堆的空间将很快被耗尽,于是回收处理无用对象便很好地解决了这个问题。
对象空间的创建和释放
创建:使用关键字new即可创建对象。
释放:将对象赋值为null即可。
垃圾回收过程
①发现无用对象;
②回收无用对象所占用的内存空间。
垃圾回收机制的相关算法
GC回收判断对象是否“死亡”有两种算法
①引用计数法
②可达性分析
①堆中的每个对象都会带有一个引用计数器,当有引用指向这个对象时计数器+1,而指向这个对象的引用失效(null)时计算器-1,最终如果计数器==0,则认为该对象无用,并进行回收。但是这个算法有个致命的缺点,就是无法处理对象相互引用。下面给出例子:
public class Student {
Student friend;
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
stu1.friend = stu2;
stu2.friend = stu1;
stu1 = stu2 = null;
}
}
stu1和stu2的计数器不为0,但他们实际上已经无用了,可是无法被识别。
②把所有的引用关系看成一张图,从一个GCroot开始,寻找相对应的引用节点,找到后就继续寻找直到找完,此时剩余的节点就是被作为没有被引用的节点,即无用节点,然后被回收。给出解释图如下
在这张图中A、B、C都与GC root有一定的引用关系,故这三个对象是有用的、存活的;而E、E、F、H跟GC root没有引用关系,所以是无用、死亡的对象。
不同的对象的生命周期不一,因此有了分代垃圾回收机制。对象有三种状态:年轻代、老年代和永久代。JVM将堆内存划分为Eden、Survivor和Tenured/Old空间。
1.年轻代
所有新生成的对象都首先放到Eden区。年轻代的目标就是尽可能快速收集掉那些生命周期短的对象,对应于Minor GC。年轻代区域存满后将放到年老代。
2.年老代
年老代一般都是些生命周期长的对象,在年轻代中经历了N(默认15)次GC后仍存活的对象,就会被放到老年代,当老年代中对象越来越多时要启动Major GC和Full GC进行一次大扫除。
3.永久代
永久代用于存放静态文件,如Java类、方法等。永久代对GC没有什么显著影响。
- Minor GC:清理年期代区域。Eden区域满了就会触发一次Minor GC,清理无用对象,将有用对象放到Survivor1/2中去。
- Major GC:清理年老代区域。
- Full GC:清理年轻代、年老代,成本高,会对系统性能产生影响
this的本质是创建好的对象的地址。因为在构造器调用前,对象已经创建,因此在构造器中也可以使用this代表当前对象。关于this的使用在前面的代码中已经提到了,不在累述。
Ps.
- 程序中产生异义时,应该使用this来指明当前对象。普通方法中,this总是指向调用该方法的对象;在构造器中,this总是指向正要初始化的对象。
- 使用this调用重载的构造器,避免相同的初始化代码。但只能在构造器中用,且必须用于构造器的第一句。
- this不能用于static方法中。
在类中,用static声明成员变量为静态成员变量,也称为类变量。类变量的生命周期和类相同,在整个程序运行期间内都有效。
- 类变量是该类的公用变量,属于类,被该类的所有实例共享,在类被载入时显式初始化。
- 对于该类的所有对象来说,static成员变量只有一份,被该类的所有对象共享。
- 一般用“类名,类属性/方法”来调用,也可以通过对象引用或类名(不需要实例化)访问静态成员。
- 在static方法中不可直接访问非static的成员。
例如
public class Student {
String name;
int age;
static String schoolname = "hnust";
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void hello(){
System.out.println("Hello");
}
public static void print(){
//hello();报错
System.out.println(schoolname);
}
public static void main(String[] args) {
Student stu = new Student("杨某人",19);
Student.print();
Student.schoolname = "HNUST";
Student.print();
schoolname = "HUNST == hnust";
Student.print();
}
}
构造器用于对象的初始化。静态初始化块用于类的初始化操作。在静态初始化块中不能直接访问非static成员。例如
public class Student {
String name;
int age;
static String schoolname;
public static void print(){
System.out.println(schoolname);
}
public static void main(String[] args) {
Student stu = new Student();
System.out.println("你好");
}
static {
System.out.println("Hello!");
schoolname = "HNUST";
print();
}
}
看到这里应该就要想起C/C++中的指针和引用了,而在Java中没有,但是我们前面提到了this关键字,this就是用来做这个事情的,例子前面已经有了,不再累述。
可以理解为C/C++中的头文件但又有不同。关于包理解起来并不难。
Package用于对类进行管理。package的使用有两个要点:
(1)它通常是类的第一句非注释性语句。
(2)包名用域名倒着写即可,再加上模块名,这样便于内部管理类。
例如下面这个包名
net.csdn.blog;
导入包
package net.csdn.blog;
例如之前就有获取键盘的输入我们就用到了Scanner,而要用Scanner就必须导入包java.util中的Scnner类。怎么导入呢?
import java.util.Scanner;
若要导入某个包下面的所有类,即
import java.util.*;//导入该包下所有的类,会降低编译速度,但不会降低运行速度
静态导入的作用是导入制定类的静态属性,以便直接使用这些静态属性。例如:
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(PI);
}
}
//就可以直接输出该包中的PI值了