目录
一:面向对象和面向过程的区别与联系
面向过程:顾名思义就是一步一步来,比如你要开一辆车,整个流程就是:点火,发动-->挂挡-->踩油门-->走你。面向过程适合简单,不需要协作的事务,重点关注如何执行。
但是当我们思考比较复杂的设计任务时,比如“如何造车”,就会发现很难列出一个具体的步骤,因为涉及的东西太多,造车复杂,需要很多协作,分类才能完成。此时面向对象的思想就派上用场了。
面向对象:针对“如何造车”这个问题进行解释,面向对象可以理解成是从“车怎么设计”这个角度去思考,而不是思考“怎么按步骤去造车”。因此,我们会把汽车分为许多部位,如轮胎,发动机,玻璃,座椅,车壳等等。为了便于协作,我们找轮胎厂完成制造轮胎的任务,发动机厂完成制造发动机的任务......各个厂家各司其职,彼此分工协作,最终进行组装,大大提高了效率。但是,具体到制造每一个精确的器件的时候,仍然具有步骤的,此时仍然离不开面向过程的思维。
因此,面向对象是从宏观上来把握,从整体上分析整个系统。但是,具体到实现部分的微观操作,仍然需要面向过程的思路去处理。
- 面向过程和面向对象思想的总结
- 都是解决问题的思维模式,都是组织代码的方式。
- 面向过程是一种“执行者思维”,解决简单问题可以使用面向过程。
- 面向对象是一种“设计者思维”,解决复杂,需要协作的问题可以使用面向对象。
- 面向对象离不开面向过程:
- 宏观上:通过面向对象进行整体的设计。
- 微观上:执行和处理数据,仍然是面向过程。
二:类的定义
(1)类由三部分组成:属性,方法,构造方法(构造器)。
每一个源文件必须有且只有一个public class,并且类名和文件名相同。
public class SxtStu{
//属性(成员变量)
int id;
String name;
int age;
//方法
void study(){
System.out.println("你好!);
}
//构造方法(方法名和类名保持一致)
SxtStu(){
}
}
(2) 构造器也叫构造方法,用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。构造器的名称应与类的名称一致。Java通过new关键字来调用构造器,从而返回该类的实例(对象),是一种特殊的方法。
声明格式:
[修饰符] 类名 (形参列表){
//具体代码
}
(3)构造器的要点
- 构造器通过new关键字调用
- 构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值。
- 如果没有定义构造器,则编译器会自动定义一个无参的构造函数。如果已经定义,则编译器不会自动添加。
- 构造器的方法必须和类名一致。
- 构造方法(构造器)也是方法,只不过有特殊的作用而已。于普通方法一样,构造方法也可以重载。
- 在用构造方法创建一个新的对象时,还可以同时实现某些功能,如输出,赋值等等
三.方法
(1)声明格式:
[修饰符1 修饰符2...] 返回值类型 方法名 (形参){
java语句;.......
}
public static int add(int a, int b, int c){ int sum = a+b+c; return sum; }
(2)方法的调用:
对象名.方法名(实参)
四.数组
(1)动态初始化(声明并开辟数组):数组的大小或数组中的数据在程序运行时才能决定,且用到new这个关键字来确定数组的大小或初始数据我们称之为动态初始化。
a.声明与开辟数组同步:数据类型 [ ] 数组名 = new 数据类型 [数组长度];
b.声明与开辟数组分步:
数据类型 [ ] 数组名; //声明数组
数组名 = new 数据类型 [length]; //初始化一个长度为length的数组
(2)静态初始化:在程序运行之前就能确定数组的大小或数组中的数据。
//简化格式
数据类型[] 数组名称 = {值,值,....} (只有基本类型数据可以这样)
//完整格式
数据类型[] 数组名称 = new 数据类型[] {值,值,....}//等式右边的[]里面为空(非基本类型数据的数组初始化只能用于声明同事赋值的情况下)
例如:
float [] stu={80.0f,70.0f,90.0f,66.5f};
int [] nums={80,70,60,50};
char [] chs={'a','b','c'};
String [] names=new String [] {"张三","李四","王五"};//等式右边的[]里面必须为空
new不new没多大关系,只要让数组变量指向一个有效的数组对象,程序即可正常使用该数组变量。
(3)
对于数组变量来说,它并不需要进行所谓的初始化,只要让数组变量指向一个有效的数组对象,程序即可正常使用该数组变量。所以,Java语言中的数组,不一定要初始化也可正常使用。
public class T { public static void main(String[] args) { //定义并初始化nums数组 int nums[] = new int[]{3,13,4,6}; //定义一个prices数组变量 int prices[]; //让prices数组指向nums所引用的数组 prices = nums; for(int i=0;i<prices.length;i++){ System.out.println(prices[i]); } //将prices数组的第三个元素赋值为100 prices[2] = 100; //访问nums数组的第三个元素 将看到数组100 System.out.println(nums[2]); } }
当程序执行prices = nums之后,prices变量将指向nums变量所引用的数组,此时prices变量和nums变量引用同一个数组对象。执行这条语句之后,prices变量已经指向有效的内存及一个长度为4的数组对象,因此程序完全可以正常使用prices变量了。
四.程序执行的内存分析
1、在内存中分为所谓的栈、堆、方法区
栈:自动分配的连续的空间,后进先出,通常用于存放局部变量
堆:不连续的空间,用于存放new出来的对象
方法区:方法区也是堆,用于存放类的信息(如代码),static变量、常量池
ps:不论是基本数据类型还是引用类型,他们都会先在栈中分配一块内存,对于基本类型来说,这块区域包含的是基本类型的内容;而对于引用类型来说,这块区域包含的是指向真正内容的指针,真正的内容被手动的分配在堆上。
通过下面代码进行内存分析
public class Person {
String name;
int age;
public void show(){
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class TestPerson {
public static void main(String[ ] args) { // 创建p1对象
Person p1 = new Person();
p1.age = 24;
p1.name = "张三";
p1.show();
// 创建p2对象
Person p2 = new Person();
p2.age = 35;
p2.name = "李四";
p2.show();
}
}
public static void main(String[] args)
从main方法开始执行,首先在虚拟机栈中建立一个main方法的栈帧,main方法也有形参,形参为String[] args。args为null;
Person p1 = new Person();
然后创建了一个新的对象p1,p1的初始值为null,于是需要在堆中为p1开辟一块新的地址空间,假设地址为0x11,p1的值即为0x11, name初始值为null,age初始值为0,show方法是一个公有的东西,所以应该将show方法的信息放在存放在方法区里的Person类里面,同时new Person( )调用了Person类的构造方法,故应在虚拟机栈中为构造方法也创建一个栈帧。Person p1 = new Person();运行结束后,构造方法的帧栈出栈。至此,Person p1 = new Person();的内存分析就算完成了。
p1.age = 24;
p1.name = "张三";
p1.show();继续往下执行,age的值为24,由于字符串不是基本数据类型,所以"张三"这个字符串应该放在Person类的常量池里,show方法的信息也存放在Person类里。p1.show( ),show方法被调用,所以应在虚拟机栈中创建一个帧栈(构造方法的帧栈已经出栈),this指当前对象p1,故地址为0x11.。p1.show( )运行结束后,其帧栈出栈。
p2的内存分析与p1一样。直到程序运行结束,所有东西都被清空。
另外栈有一个很重要的特殊性,就是存在栈中的数据可以共享。如 需要定义int a = 3; int b =3;这两个自动变量。编译器先处理int a=3;首先在栈中创建一个变量为a的引用,然后查找栈有没有字面值为3的引用,没有找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b=3;在创建完b这个引用变量后,由于在栈中已经有了3这个字面值,即将b直接指向3的地址。这样,就出现了a和b同事指向3的情况。 定义完a与b后,在令a=4,那么b不会等于4,而是等于3,。在编译器内部,遇到时,它就会中新搜索栈中是否有4这个字面值,如果没有,重新开辟地址存放4的值。如果已经有就直接将a指向这个地址,因此a的值改变不会影响b的值。
五:equals与==的用法
栈区存引用和基本类型,不能存对象,而堆区存对象。==是比较地址,equals()比较对象内容。
== 是比较内存地址是否相同,而equals 是针对引用数据类型的方法,取出内存地址中的值比较是否相同。
(1)要判断两个基本数据类型的变量是否相等,只能用==不可以用equals。基本数据类型无equals方法,因为所有的基本数据类型不存在“引用”的概念,基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面,Java语言里面八种数据类型(byte,short,int,long,float,double,char,boolean)是这种存储模型;
(2)String类
String s1 = "latiny"; String s2 = "latiny"; System.out.println(s1 == s2); System.out.println(s1.equals(s2)); String s3 = new String("latiny"); String s4 = new String("latiny"); System.out.println(s3 == s4); System.out.println(s3.equals(s4)); 输出结果: true true false true
String str1 = "abcd"的实现过程:首先栈区创建str引用,然后在String池(独立于栈和堆而存在,存储不可变量)中寻找其指向的内容为"abcd"的对象,如果String池中没有,则创建一个,然后str指向String池中的对象,如果有,则直接将str1指向"abcd"";如果后来又定义了字符串变量 str2 = "abcd",则直接将str2引用指向String池中已经存在的“abcd”,不再重新创建对象;当str1进行了赋值(str1=“abc”),则str1将不再指向"abcd",而是重新指String池中的"abc",此时如果定义String str3 = "abc",进行str1 == str3操作,返回值为true,因为他们的值一样,地址一样,但是如果内容为"abc"的str1进行了字符串的+连接str1 = str1+"d";此时str1指向的是在堆中新建的内容为"abcd"的对象,即此时进行str1==str2,返回值false,因为地址不一样。
String str3 = new String("abcd")的实现过程:直接在堆中创建对象。如果后来又有String str4 = new String("abcd"),str4不会指向之前的对象,而是重新创建一个对象并指向它,所以如果此时进行str3==str4返回值是false,因为两个对象的地址不一样,如果是str3.equals(str4),返回true,因为内容相同。