类和对象的简单概念:
- 类是对某一类事物的描述,是抽象的概念上的定义;对象是实际存在的该类事物的个体,也称为实例;
例如:画上的人物画像可以看作一个类,而依照画图的人体模特就是一个真实存在的对象;不要过于纠结,否则越陷越深
- 类的声明与定义:
[标识符] class 类名称{
//类的成员变量
//类的方法
}
static修饰的成员变量不能访问没有static修饰的成员。
2、对象的声明与使用:
类名 对象名 = new 类名();//类产生对象的基本形式
3,对象实例化的过程如下:
4,匿名对象:
指没有名字的对象,对象真正有用的部分是在堆内存里面,而栈内存只是保存了一个对象的引用名称(严格讲是对象在堆内存的地址)所以所谓的匿名对象就是指,只开辟了堆内存空间,而没有栈内存指向的对象;
例:new person().say();
匿名对象有两个特点:
1)。匿名对象是没有被其他对象所引用,即没有栈内存指向;
2)。由于匿名对象没有栈内存指向,所以只能使用一次,之后就变成无法找寻的垃圾对象,就会被垃圾回收器收回;
5,对象的比较
有两种方式:通过==和equals方法进行对象的比较
==运算符比较两个对象的内存地址值(引用值)是否相等,equals()方法用于比较两个对象的内容是否一致;
当创建一个对象后,jvm就会给这个对象分配一个自身的引用——this,this代表对象(比如‘我’代表我这个人)静态属性及静态方法属于类,与对象无关,所以静态属性及静态方法是没有this的。
6.栈内存和堆内存的区别:
在java中,栈(stack)是由编译器自动分配和释放的一块内存区域,主要用于存放一些基本数据类型的变量,指令代码,常亮及对象句柄(也就是对象的引用地址)。栈内存的操作方式类似于数据结构中的栈(仅在表尾进行插入和删除操作的线性表)。栈的优势在于,他的存取速度比较快,仅次于寄存器,栈中的数据可以共享。缺点表现在,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆(heap)是一个程序运行动态分配的内存区域,在java中,构建对象时所需的内存从堆中分配。这些对象通过new指令“显示”建立,放弃分配方式类似于数据结构中的链表。堆内存在使用完毕后,是由垃圾回收器(Garbage Collection)“隐式”回收的。堆内存的优势在于动态的分配内存大小,可以按需分配,其生存期也不必事先告诉编译器,在使用完毕后,java的垃圾收集器会自动收走这些不再使用的内存块。其缺点是,由于在运行时才动态分配内存,相比于栈内存,他的存取速度较慢。
Stack Overflow(http://stackoverflow.com/)如果有it问题,可以使用这个网站查找高质量的英文解答。
十,重复调用的代码块--方法
1.方法的基本定义:所有方法均在类中定义和声明。定义一个方法的语法如下所示:
修饰符 返回值类型 方法名(参数列表)
{
//方法体
return 返回值;
}
方法包含一个方法头和方法体
方法名和参数列表共同构成方法签名,来标识方法的身份信息。编译器可以通过不同的方法签名来区分不同的方法。这种使用方法名相同但参数列表不同的方法签名机制,称之为方法的重载。在调用的时候,编译器会通过参数的类型或个数的不同来执行不同的方法体代码。示例代码可以参考String类中对valueOf方法的重载;
方法签名仅包括方法名和参数,因此方法重载不能根据方法的不同返回值来区分不同方法,因为返回值不属于方法签名的一部分,例如,int add(int,int)和void add(int,int)的方法签名是相同的,编译器会认为这两个方法完全相同而无法区分,故他们无法达到重载的目的。
2.构造方法
在Java程序里,构造方法所完成的主要工作是对新创建对象的数据成员赋初值。可将构造方法视为一种特殊的方法,定义方式如下:
class 类名称
{
访问权限 类名称(类型1 参数1,类型2 参数2,……)//构造方法
{
程序语句;
//构造方法没有返回值
}
}
在使用构造方法时候需要注意几点:
1)构造方法名称和所属的类名必须一致。
2)构造方法没有返回值,也不可以使用void。
3)构造方法也可以像普通方法一样被重载。
4)构造方法不能被static和final修饰
5)构造方法不能被继承,子类使用父类构造方法需要使用super关键字。
构造方法的调用时机业余普通方法有所不同,普通方法是在使用时才调用,而构造方法则是在创建对象时就自动隐式执行,因此,构造方法无需在程序中直接调用,而是在对象产生时自动执行一次。通常用它来对对象的数据成员进行初始化。
3.构造方法私有化
私有化构造方法有什么好处?
可以考虑下面的特定需求场景:如果要限制一个类对象产生,要求一个类只能创建一个实例化对象,该怎么办?
我们知道,实例化对象需要调用构造方法,但如果将构造方法使用private藏起来,则外部肯定无法直接调用,那么实例化该类对象就只能有一种途径——在该类内部使用new关键字创建类实例。这样就能确保一个类只能创建一个实例化对象。在软件工程中,这种设计模式称之为单例模式(Singleton Design Pattern)。
构造方法的私有使用范例2:
01 public class TestSingleDemo
02 {
03 public static void main(String[]args)
04 {
05 //声明一个Person类的对象
06 Person p;
07 //虽私有化Person类的构造方法,但可通过Person类公有接口获得Person实例化对象
08 p=Person.getPerson();
09 System.out.println("姓名:"+p.name);
10 }
11 }
12 class Person
13 {
14 String name;
15 //在本类声明Person对象PERSON,注意此对象用final标记,表示该对象不可更改
16 private static final Person PERSON=new Person();
17 private Person()
18 {
19 name="kehr";
20 }
21 public static Person getPerson()
22 {
23 return PERSON;
24 }
25 }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
【运行结果】
【代码详解】
第06行声明一个Person类的对象p,但并未实例化,仅是在栈内存中为对象引用p分配了空间存储,p所指向的对象并不存在。
第08行调用Person类中的getPerson()方法,由于该方法是公有的,可以借此方法返回Person类的实例化对象,并将返回对象的引用赋值给p。
第17~20行将Person类的构造方法通过private关键字私有化,这样外部就无法通过其构造方法来产生实例化对象。
第16行在类声明了一个Person类的实例化对象,此对象是在Person类的内部实例化,所以可以调用私有构造方法。此对象被标识为static类型,表示为一静态属性。另外,在声明Person对象的时候还加上了一个final关键字,此关键字表示对象PERSON不能被重新实例化。
【范例分析】
由于Person类构造方法是private,所以如 Person p = new Person () 已经不再可行了(06行)。只能通过“p = Person.getPerson();”来获得实例,而由于这个实例PERSON是static的,全局共享一个,所以无论在Person类的外部声明多少个对象,使用多少个“p = Person.getPerson();”,最终得到的实例都是同一个。也就是说,此类只能产生一个实例对象。这种做法就是上面提到的单态设计模式。所谓设计模式也就是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。
4,方法的递归调用
在程序设计领域,递归是指函数直接或间接调用自身的一种特性,如下所示。递归调用能够大大减少代码量,将原本复杂的问题简化成一个简单的基础操作来完成。
递归一定要有结束条件,这又被称作递归出口。
下面我们通过一个例子来说明方法递归的调用原理。下面以计算“1+2+3+…+n”的值为例,分别实用非递归(nonrecursion)和递归(recursion)方式实现,读者可体会二者的区别。
【范例10-14】 计算“1+2+3+…+n”,递归和非递归实现(RrecursionMethod. java)。
01 public class RecursionMethod
02 {
03 //非递归方式实现计算“1+2+3+…+n”
04 public int addNonrecursion(int n)
05 {
06 int result=0;
07
08 for(int i=1;i<=n;++i)
09 {
10 result+=i;
11 }
12 return result;
13 }
14
15 //递归方式实现计算“1+2+3+…+n”
16 public int addRecursion(int n)
17 {
18 //递归出口,当n小于1时函数就会逐层返回
19 if(n<=1)return n;
20 return n+addRecursion(n-1);
21 }
22
23 public static void main(String[]args)
24 {
25 RecursionMethod test=new RecursionMethod();
26 //调用非递归方法计算
27 int result=test.addNonrecursion(10);
28 System.out.println("非递归计算1+2+3+...+10结果:"+result);
29 //调用递归方法计算
30 result=test.addRecursion(10);
31 System.out.println("递归计算1+2+3+...+10结果:"+result);
32 }
33 }
【运行结果】
保存并运行程序,结果如下图所示。
【代码详解】
第04~13行,定义一个非递归的函数addNonrecursion,采用for循环的方式来计算结果。
第16~21行,定义一个递归函数addRecursion,采用递归的方式计算结果。
第25行,实例化一个本类的对象test。
第27行,调用非递归函数,计算1+2+3+...+10结果。第28行,输出计算结果。
第30行,调用递归函数,计算1+2+3+...+10结果。第31行,输出计算结果。
【范例分析】
对于非递归的实现,用到了第7章的for循环语句,以1为基数不断循环叠加最终得出累加的结果。而在递归实现的操作中则明显不同。解决这个问题的思路是,若要计算addRecursion (n)=1+2+3+...+n结果,需要求{1+2+3+…+( n-1 )}+n;而计算1+2+3+…+( n-1 )的值,本质上就是计算addRecursion (n-1)的值。而计算addRecursion (n-1)的值,本质上就是{1+2+3+…+( n-2 )}+( n-1 ),而计算{1+2+3+…+( n-2 )}的值,本质上就是计算addRecursion (n-2),……,直 到 addRecursion (1)=1,然
后 逐 级 返 回,addRecursion (2)=addRecursion (1)+2=1+2=3,addRecursion (3)= addRecursion (2)+3 = 3+ 3 =6,……,直 到 addRecursion (n)=addRecursion (n-1)+n,计算完毕。
递归通过对方法本身的压栈和弹栈的方式,将每一层的结果逐级返回,通过逐步累加求得结果。下图给出addRecursion(5)的计算过程。
由上图可以得知,递归方式求解问题分为两个阶段:(1)第1个阶段是回推,逐级推导本级计算所需的条件。例如,如果需要计算addRecursion (n),需要计算addRecursion (n-1) +n,而计算addRecursion (n-1),需要计算addRecursion(n-2)+ (n-1)……一直回推到addRecursion (1)=1,这是递归的终止条件。然后开始第2个阶段。(2)第2个阶段是递推。addRecursion(2) =addRecursion(1)+2=3, addRecursion(3) = addRecursion(2)+3=6,……,直 到addRecursion(n) =addRecursion(n-1)+n,问题得以求解。
提示
虽然递归操作有许多的优点,但是缺点也很明显。使用递归实现需要函数压栈和弹栈的操作,所以程序的运行速度比不用递归实现要慢的多。如果操作不慎还极易出现死循环,读者编写代码过程中需要多加注意,一定要设置递归操作的终止条件。
5,代码块
代码块:使用“{}”将多行代码封装在一起,形成独立的代码区域;如:
{
//代码块
}
代码块有四种:1)普通代码块;2)构造代码块;3)静态代码块;4)同步代码块;代码块不能独立运行,需要依赖于其他配置。
1)普通代码块
最常见的代码块,在方法名后(或方法体内)用一对{}括起来的数据块就是普通块,但不能单独存在类中;如:
public class Demo{
public static void main(String arg[]){//main方法后的第一对大括号,是一个普通代码块
{}//第二个普通代码块;
}
}
2),构造代码块
在类中直接定义,没有任何前缀,后缀以及修饰符的代码块;
构造代码块特性:构造代码块和构造方法一样是在对象生成时被调用,但它的调用时机比构造方法还要早;
构造代码块作用:由于构造代码块的这种特性,构造代码块可用来初始化成员变量。如果一个类中有多个构造方法,并且都需要初始化成员变量,那就可以把构造方法中相同的代码抽取出来,放到构造代码块中,可以大大减少不同构造方法中重复的代码,提高代码复用性。
构造代码块中的初始化是一个类的所有构造方法都共有的“交集”部分,具有个性化特征的初始化还是要放在各自的构造方法中。
3),静态代码块
使用static关键字加以修饰并用大括号{}“括起来的代码块称为静态代码块,主要用来初始化静态成员变量,是最早执行的代码块;如:
public class Demo{
//静态代码块
static{
System.out.println("先执行静态代码块");
}
//构造方法
public Demo{
System.out.println("创建对象时执行");
}
//构造代码块
{
System.out.println("在构造方法前执行");
}
}
静态代码块优先于构造方法的执行,并且不管有多少实例化对象产生,静态块都执行一次。利用这种特性,静态代码块可以被用来初始化类中的静态成员变量。静态成员变量属于类对象共享的,因此不会受到创建对象个数的影响。
可以得出初步结论,在执行时机上,静态代码块在类加载时就会执行,因此早于构造代码块,构造方法。当静态代码块和main方法属于一个类时,静态代码块比main方法执行早,静态块的执行级别是最高的。
6,方法与数组
在java数组中存放的是一组类型相同的数据序列,并通过下标对数组元素进行访问。在基本数据类型构成的数组的基础上,抽象出了数组对象,整合具有相同特点的数据,再进一步扩展,便构成功能更加强大的集合,从本质上说,他们都是存储数据的容器。与存储单个数据的变量相比,他们存储的是多个数据,或是多组数据。
1),数组引用传递
在java语言中,b=a(a和b都是引用名)的含义就是将a起个别名”b“,之后,a和b其实就是指向的是同一个对象。在java中,这种给变量取别名的机制称为引用。
因为数组是对象,在对数组进行传参时,参数传递形式为”传引用“。这时实参和形参指向同一块内存空间。当对形参所指向的数组数据进行修改时,都会同步影响到实参所指向的数组数据(形参和实参本质上就是一块内存区域)。由于整形形参和实参之间是”传值“关系,在实参将值赋值给形参后,形参和实参二者没有任何关联,对于“值传递”形参和实参所指向的是完全不同的内存空间。
2),在方法中实现排序
排序算法有很多,比较著名的有快速排序算法,冒泡排序算法,选择排序算法,归并排序算法等。再合适的场景选择合适的算法能够提高程序性能。
演示使用冒泡排序对数组排序:
public void sort(int[] arr){
//冒泡排序算法
for(int i = 0;i<arr.length;i++)
{
for(int j = i+1;j<arr.length;j++)
{
if(arr[i]<arr[j])
{
//将两个值互换位置
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
}
由于sort方法的参数传递方式是数组对象的引用,故此,在方法体中对数组的所有修改,都会主函数对象arr中体现出来,所以sort函数不需要返回值。在sort方法中,数组使用“传引用”方式,保证sort内的数组arr和main中的数组arr本质上是同一个数组对象(即引用指向的是同一块内存空间),这样在sort方法中完成排序后,就不用显示的在返回一个数组了。
3),让方法返回数组
此处不做演示,不管如何改变,实际上,数组对象引用的核心功能没有改变,都是一块堆内存设置了多个栈内存指向——一个数组对象(存储在堆内存中)对应多个别名(引用,存在栈内存中)。
可以体会到,通过引用对数组对象进行操作,有许多便利,能够减少代码量,简化程序逻辑,同时也减少数据传输的开销,提高程序的运行效率。
4),与数组有关的操作方法
由基本数据类型构建的数组,如int[],double[]等,java提供的内置方法并不是很多,做常用的方法是clone()方法,会将数组复制一份,返回一个新的引用,常用来复制数组。数组对象提供的length属性用于记录数组的长度,数组中包含元素的个数。
int[] arr = {1,2,3};
int[] arrnew = arr.clone(); //arrnew克隆arr的数组元素
因为arrnew属于重新在堆内存中开辟一块内存空间,如果进行arr==arrnew比较的话,输出的结果为false,虽然二者的元素相同,但二者的影用值是不同的,也就是说,他们分别指向不同的堆内存地址。
如果是不使用clone()方法,而直接进行赋值操作int[] arrnew = arr;再进行arr==arrnew比较时,结果为true;
java的所有类都默认继承java.lang.Object类,在该类中有一个方法clone()。这个方法将返回Object对象的拷贝。有两点需要特别说明:
a,“克隆”(拷贝)对象返回的是一个新对象,而不是一个已有对象的引用。
b,“克隆”对象与用new操作符返回的新对象是有区别的,克隆对象是拷贝某个对象的当前信息,而不是对象的初始信息。比如:如果把new构造出来的对象比喻成刚出生的“小羊羔”,过一段时间,“小羊羔”长成一个“小山羊”了。这时,如果通过克隆操作,得到的是另一个一模一样的“小山羊”,而不是当初new构造出来的“小羊羔”。
5),数组的排序
在上面我们使用自定义的方法对数组进行了排序,事实上在java成熟的开发库中,java为我们提供了一个名为sort()从小打到的排序方法,可以直接调用该方法就能实现对数组的排序操作,而不需要我们重新实现一个新方法。如:
int[] arr = {1,2,3};
java.util.Arrays.sort(arr);//java包库提供的排序方法
十一,枚举
枚举是一个被命名 的整型常数的集合。
枚举的定义:
enum 枚举名 {枚举值表};
enum是枚举关键字 枚举值表是需要罗列的所有可用值,也被称为枚举元素;
定义枚举变量有三种格式:
1.先定义,后说明
enum WeekDay {sun,mon,tue,wed,thu,fri,sat}; //先定义
enum WeekDay a,b,c; //后说明
2,enum WeekDay {sun,mon tue,wed,thu,fri,sat} a,b,c; //定义并说明
3,enum {sun,mon,tue,wed,thu,fri,sat} a,b,c; //直接说明,定义为无名枚举
持续更新中!!!