——- android培训、java培训、期待与您交流! ———-
面向对象
面向对象是相对于面向过程而言的,它早期是面向过程,经过不断演化到了现在的面向对象。
1.面向过程与面向对象的异同
相同的地方是面向对象与面向过程都是一种思想,是思考问题的一种思考方式。
不同点:
面向过程:强调功能的行为
面向对象:将功能封装进对象,强调具备了功能的对象
面向对象是基于面向过程的。
2.什么是面向对象?
面向对象是一种对现实世界理解和抽象的方法,它是把数据及对数据的操作方法放在一起,作为一个相互依存的整体。
在对面向对象的理解中有这么一句:万物皆对象(Everything is Object)
3.面向对象的特点有哪些?
1)让复杂的问题简单化。
2)它让我们从面向过程中的执行者变成了指挥者。
3)它的这种思想是符合现在人们思考习惯的一种思想。
例如:我要看电视,我只要把电源插上,打开电视就可以了。至于电视是怎么打开的,怎么播出图像的,我们不用去了解,只需要用电视播放图像的这个功能就可以了。这就相当于我们指挥电视放节目,电视执行它的功能。
过程和对象在我们的程序中是如何体现的呢?
过程的体现就是函数;对象是将函数等一些内容进行了封装。
4.面向对象的三大特征
1)封装
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。
好处:将变化隔离,便于使用,提高了重用性,安全性。
封装原则:将不需要对外提供的内容都隐藏起来,把属性也都隐藏,只提供公共方法对其访问。
函数就是一个最小的封装体。
private(私有)关键字:
1.是一个权限修饰符
2.用于修饰成员(成员变量和成员函数)
3.被私有化的成员只在本类中有效
私有的成员:其他类不能直接创建对象访问,所以只有通过本类对外提供具体的访问方式来完成对私有的访问,可以通过对外提供函数的形式对其进行访问。
好处:可以在函数中加入逻辑判断等操作,对数据进行判断等操作,提高对数据访问的安全性。
常用的方式:将成员变量私有化,对外提供对应的set,get方法对其进行访问。
注意:私有仅仅是封装的一种表现形式。 封装不是私有。
2)继承
继承关键字:extends, 都是子类继承父类(也叫超类)。
好处:
1.提高了代码的复用性。
2.让类与类之间产生了关系,有了这个关系,才有了多态的特性。
父类的由来:其实是由多个类不断向上抽取共性内容而来的。
java中对于继承只支持单继承(就像一个人他只能有一个父亲)。
java虽然不直接支持多继承,但是保留了这种多继承机制,进行改良。
单继承:一个类只能有一个父类。 多继承:一个类可以有多个接口。
java中为什么不支持多继承呢?
因为当一个类同时继承两个父类时,如果两个父类中有相同功能,那么子类对象调用该功能时,运行哪一个呢。父类中的方法是存在方法体的。
但是java支持多继承(就像一个父亲可以有多个孩子或孙子)。如:A继承B B继承C C继承D
多重继承的出现,就有了继承体系。体系中的顶层父类是通过不断向上抽取而来的。它里面定义的是该体系最基本最共性的内容的功能。
所以,一个体系要想被使用,直接查阅该系统中的父类功能即可知道该体系的基本用法。
如果想要使用一个体系,就需要建立对象。建议建立最子类对象,因为最子类不仅可以使用父类中的功能,还可以使用子类特有的一些功能。
简单来说:对于一个继承体系的使用,就是查阅顶层父类中的内容,创建最底层子类的对象。
子父类出现后,类中成员的特点:
类中成员有:1.成员变量 2成员.函数 3.构造函数
1.成员变量
当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。
如果想要调用父类中的属性值,就要用super关键字。
this:代表的是本类对象的引用。
super:代表的是父类对象的引用。
注意:子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
2.成员函数
当子类出现和父类一模一样的函数时,子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。
这种情况就是函数的另一个特性:函数的覆盖(也叫重写)。
什么时候用覆盖呢?
当子类继承父类,沿袭了父类的功能,到子类中。但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。所以当一个类的功能内容需要修改时,可以通过覆盖来实现。
覆盖:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2.静态只能覆盖静态。
记住:
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。返回类型也要一样。
3.构造函数
我们发现子类构造函数运行时,先运行了父类的构造函数。也就是在对子类对象进行初始化时,父类的构造函数也会运行,为什么呢?
因为子类的所有构造函数中默认第一行都有一条隐式的语句super();
super();:表示父类的构造函数,并会调用参数相对应的父类中的构造函数。而super():是在调用父类中空参数的构造函数。
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数,这就是子类的实例化过程。
注意:
super();一定要定义在子类构造函数的第一行。子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的super();语句。
如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问父类中的构造函数。
如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。
小知识:如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。调用构造函数super(); 调用一般函数super.函数名。
问题:
1.super()和this()是否可以同时出现的构造函数中?
不可以,因为它们都只能定义在第一行。
2.super()或者this():为什么一定要定义在第一行?
因为super();或this();都是调用构造函数的,而构造函数用于初始化,初始化的动作要先完成。
什么时候使用继承呢?
当类与类之间存在着所属关系时,才具备了继承的前提。如:a是b中的一种,a继承b。就像豹是猫科中的一种,在英文里所属关系就是“is a”。
所以判断所属关系,可以简单的看做如果继承后,被继承类中的功能都可以被子类所具备,那么继承成立。如果不是,不可以继承。
注意:千万不要为了获取其他类的功能、简化代码而继承。
类为什么要继承类?
是因为父类里有非抽象内容可以直接拿过来用。
在方法覆盖时要注意两点:
1.子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限才可以实现继承。否则,编译失败。
2.覆盖时,要么都静态,要么就都不静态。因为静态只能覆盖静态,或者被静态覆盖。
继承的一个弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者复写的。
如何解决这个问题呢?这就用到了final关键字。
其实这样的原因的就是给一些固定的数据起个阅读性较强的名称。
那么不加final修饰不是也可以使用吗?如果不加final这个值就是一个变量,是可以更改的。加了final,程序更为严谨。
3)多态
定义:某一类事物的多种存在形态,也可以理解为事物存在的多种体现形态。
体现:父类引用或者接口的引用指向了自己的子类对象。例如:Animal a = new Cat();动物是猫具体事物中抽取出来的父类型。这就是父类型引用指向了子类对象。父类的引用也可以接受自己的子类对象。
前提:必须是类与类之间有关系,要么是继承,要么就是实现。通常还有一个,就是覆盖。
好处:提高了程序的扩展性。
弊端:当父类引用指向子类对象时,虽然提高了扩展性,但只能访问父类中具备的方法,不可以访问子类中特有的方法。(也就是前期不能使用后期产生的功能,即访问的局限性)
多态的出现思想上也做着变化:以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。
如何使用子类特有方法?
Animal a = new Cat();//这称为类型提升,也叫向上转型。
如果想要调用Cat的特有方法时,如何操作?
强制将父类的引用转成子类类型。也就是向下转型。
转换格式:父类 x = new 子类(); //向上转型 子类 y =(子类)x;//向下转型
规律:多态自始自终都是子类对象在做着变化。
引用数据类型的判断
判断某一类型引用指向的对象到底符合什么类型的时候用关键字instanceof
例子:if(a instanceof Cat)
instanceof:用于判断对象的类型。
格式:<对象 instanceof 类型(类类型或接口类型)>,判断一个对象是否所属于指定的类型。
Student instanceof Person = true;//student继承了person类
多态核心思想:我们对类型进行抽取,导致多态的产生,操作同一个的大类型对大类型中的所有小类型都能进行操作。 因为这个思想才提高了扩展性。
多态在子父类中的成员上体现的特点:
1.成员变量:在多态中,子父类成员变量同名。
编译时期:参考是引用型变量所属的类中是否有调用的成员。(编译时不产生对象,只是检查语法是否错误)
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
简单来说:成员变量在编译运行都看左边。
2.成员函数
编译时期:参考引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
运行时期:参考对象所属的类中是否有调用的方法。
那么这是为什么呢?
因为在子父类中,对于一模一样的成员函数,有一个特性,就是覆盖。
简单来说:成员函数在多态调用时编译看左边,运行看右边。
3.静态函数
编译时期:参考的是引用型变量所属的类中是否有调用的成员。
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
那么这是为什么呢?
因为静态方法其实不属于对象,而是所属于该方法所在的类。
调用静态的方法引用是哪个类的引用调用的就是哪个类中的静态方法。
简单来说:静态函数在多态调用时编译运行都看左边。
5.类与对象
类(Class):实际上是定义同一类所有对象的变量和方法的蓝图或原型,它表示对现实生活中一类具有共同特征的事物的抽象,也就是对现实生活中事物的描述。
对象:就是这类事物,实实在在存在的个体。在现实中我们可以把任何东西看做是对象。
因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象。通常通过调用类的一个构造函数来创建它。
那么,是先有的类还是先有的对象呢?
对于计算机语言来说是先有类后有对象,因为计算机是要先描述才产生对象,而对于现实来说是先有对象后类,因为我们是先看到对象后才想去描述它。 描述在java中是用类的形式来体现的。
我们在描述什么呢?
其实就是描述这个事物的属性与行为。(属性对应是类中的变量,行为对应的类中函数(也就是方法)。)
而定义类,其实就是在描述事物,就是在定义属性和行为。属性和行为又共同成为类中的成员(成员变量和成员方法(成员函数))。
在类中定义其实都称之为成员。成员有两种:
1.成员变量:其实对应的就是事物的属性。
2.成员函数:其实对应的就是事物的行为。
成员变量和局部变量的区别:
1.成员变量直接定义在类中。
局部变量定义在方法中,参数上,语句中。
2.成员变量在本类中都有效。
局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。
3.成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。
局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。
对象在java是什么样的呢?
对象在java中通过一个new操作符会产生一个实体,这个实体是在堆内存中。也就是说具体对象就是对应java在堆内存中用new建立的实体。
所以把所描述的事物映射到内存中的一个是类,一个是实体。
那什么可以称之为实体呢?
凡是用来存储多个数据的我们就称之为实体。它们都放在堆内存中。
6.匿名对象
匿名对象:在一个对象被创建之后,调用对象的方法时可以不定义对象的引用变量,而直接调用这个对象的方法,也就是没有明确的声明的对象。
可以说匿名对象是对象的简化形式。
匿名对象的两种使用情况:
1.当对对象方法只进行一次调用的时候,可以使用匿名对象。
2.匿名对象可以作为实际参数进行传递。
示例:
Car c = new Car();
c.num = 5;
上面这两句可以简写成一句:new Car().num = 5;//这句就是匿名对象
注意:调用方法匿名对象比较有意义,而调用属性是没意义的。如果对一个对象进行多个成员调用,必须给这个对象起个名字。
匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。如示例。
匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。如:方法名(new Car());
7.构造函数
构造函数:用于给对象进行初始化,是给与之对应的对象进行初始化,它具有针对性,函数中的一种。
对象在一建立时就会调用与之对应的构造函数。
构造函数特点:
1.该函数名称与所在类名称相同。
2.不用定义返回值类型。
3.该函数没有具体的返回值,不可以写return语句。
作用:给对象进行初始化。 所有对象创建时,都需要初始化才可以使用。
注意:
1.默认构造函数的特点。
2.多个构造函数是以重载的形式存在的。
构造函数的小细节:当一个类在定义时,如果没有定义构造函数,那么该类系统会自动生成一个空参数的构造函数,这是为了方便该类创建对象,完成初始化。如果在类中定义了构造函数,那么默认的构造函数就没有了。
构造函数和一般函数有什么区别呢?
1.在写法上不同,也就是两个函数定义格式不同。
2.构造函数是在对象创建时就被调用、执行,给对象初始化。而一般函数是对象创建后,在需要调用时才执行,是给对象添加具备的功能。
3.在一个对象建立,构造函数只运行一次。而一般函数可以被该对象调用多次。
什么时候定义构造函数呢?
当分析事物时,该事物一出现就具备一些特性或行为,那么将这些内容定义在构造函数中。
构造函数和构造代码块有什么区别?
构造函数:给对应的对象初始化,具有针对性。
构造代码块:给所有的对象进行初始化,也就是所有对象都会调用一个代码块。只要对象一建立就会调用这个代码块。
格式:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
{
构造代码块//定义不同对像共性的初始化内容
}
作用:给所有对象进行初始化。
对象的初始化过程
示例:Person p = new Person(“赵二”,18);
该段代码都做了什么呢,创建一个对象都在内存中做了什么呢?
1.因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中。也就是先将硬盘上指定位置的Person.class文件加载进内存。
2.执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
3.执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。
4.在堆内存中开辟一个实体空间,分配了一个内存地址。也就是new。
5.在堆内存中建立对象的特有属性。并进行默认初始化。
6.对空间中的属性进行显示初始化。
7.对对象进行构造代码块初始化。
8.调用该实体对应的构造函数,进行构造函数初始化。
9.将内存地址赋给栈内存中的p变量,p变量就引用了该实体。也就是指向了该对象。
8.this
是区分成员变量和局部变量同名的情况。
this到底代表什么呢?
this代表本类的对象。
但是到底代表本类中哪一个对象呢?
this代表它所在函数所属对象的引用。简单说就是哪个对象在调用this所在的函数,this就代表哪个对象。
开发时,什么时候使用this呢?
当定义类中功能时,如果该函数内部要用到调用该函数的对象,用this表示这个对象。但凡本类功能内部使用本类对象,都用this表示。
注意:构造函数间的调用只能用this语句。用this调用构造函数,必须定义在构造函数的第一行。因为构造函数是用于初始化的,初始化要先执行,而且只执行一次。如果不放到第一行会报错。编译会失败。
一般函数是不能直接调用构造函数的,因为this语句是不能用在一般函数中的,只能用在构造函数间。但要防止死循环情况。就是构造函数互相调用。
示例:A(){
this(“aaa”);
}
A(String name){
this();
}
调用格式:this(实际参数);
1.this.成员属性或成员方法(一般方法);
2.this(本类中的对应参数的构造函数);
9.static
static关键字:是一个修饰符。
用法:用于修饰成员(成员变量和成员函数),不能修饰局部。
被修饰后的成员具备的特点:
1.随着类的加载而加载 也就是说:静态会随着类的消失而消失。说明它的生命周期最长。
2.优先于对象存在 明确一点:静态是先存在。对象是后存在。
3.被所有对象所共享。
4.除了可以被对象调用外,还可以直接被类名调用 格式:类名.静态成员;
使用注意:
1.静态方法只能访问静态成员,不可以访问非静态成员。而非静态方法既可以访问静态也可以访问非静态。
因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。
2.静态方法中不可以使用this,super(代表父类引用,也就是父类对象)关键字。
因为静态方法加载时,优先于对象存在,所以静态方法中不可以出现this。
3.有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。
这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。
4.主函数是静态的。
静态的利弊:
利:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名调用。
弊:生命周期过长。访问出现局限性。(静态虽好,只能访问静态,而非静态谁都能访问)
什么时候使用静态呢?
从两方面来看:因为静态修饰的内容有成员变量和成员函数。
什么时候定义静态变量(类变量)?
当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数?
当功能内部没有访问到非静态数据(也就是对象特有数据),那么该功能可以定义成静态函数。
成员变量与静态变量(类变量)的区别:
1.成员变量所属于对象。所以也称为实例变量。
静态变量所属于类。所以也称为类变量。
2.存放位置
成员变量随着对象的建立而存在于堆内存中。
静态变量随着类的加载而存在于方法区中。
3.生命周期
成员变量随着对象创建而存在。随着对象被回收而消失。
静态变量随着类的加载而存在。随着类的消失而消失。
4.成员变量只能被对象所调用 。
静态变量可以被对象调用,也可以被类名调用。
所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。
方法区(也叫共享区或数据区)。里面放的是类中的方法,类中的共享数据
如:String name;//这样的叫成员变量,也叫实例变量(对象的变量)。
static String person;//这样的叫静态的成员变量,也叫类变量。
静态的应用:
每一个应用程序中都有共性的功能,我们可以将这些共性功能进行抽取,独立封装,以便复用。
这个抽取出来共性功能的类我们可以称为工具类,虽然我们可以通过建立工具类的对象使用里面的方法。
但就有了问题:
1.对象是用于封装数据的,可是工具类对象并未封装特有数据。
2.我们操作的每一个方法都没有用到工具类对象中的特有数据。
这时就要考虑,让程序更严谨是不需要对象的。
我们就可以将工具类中的方法都定义成static的,直接通过类名调用即可。
通常情况下工具类都定义成静态方法
好处是:将方法都静态后,可以方便于使用。
注意:但是该类还是可以被其他程序建立对象的,所以为了更为严谨,可以强制让该类不能建立对象。
可以通过将构造函数私有化完成。
构造函数私有化示例:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
class A
{
private A(){}
private A a = new A();
}
静态代码块:就是一个有静态关键字标识的一个代码块区域。定义在类中。
格式示例:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
static
{
静态代码块中的执行语句。
}
作用与特点:可以完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次(new 多个对象也就只执行一次)。如果和主函数在同一类中,优先于主函数执行。
10.final
final:最终,作为一个修饰符。
1.final可以修饰类,方法,变量。
2.final修饰的类不可以被继承。为了避免被继承,被子类复写功能。
3.final修饰的方法不可以被覆盖。被复写。
4.final修饰的变量是一个常量。只能被赋值一次,既可以修饰成员变量,又可以修饰局部变量。
5.内部类只能访问被final修饰的局部变量。也就是说内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
什么时候用final呢?
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便阅读。而这个值不需要改变,所以加上final修饰。
作为常量:常量的书写规范所有字母都大写,如果由多个单词组成。单词间通过”_”连接。 被 public static final修饰的常量可以叫做全局常量。
11.内部类
一个类里可以定义多个内部类。
什么是内部类呢?
将一个类定义在另一个类的里面,对里面那个类就称为内部类(也可以叫内置类或嵌套类)。
内部类的访问规则:
(1)内部类可以直接访问外部类中的成员,包括私有成员。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式:外部类名.this
(2)而外部类要访问内部类中的成员必须要建立内部类的对象。
访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。可以直接建立内部类对象。
别的外部类直接访问内部类中的成员格式:
外部类名.内部类名 变量名 = 外部类对象.内部类对象;(new 外部类名().new 内部类名();)//这是直接访问内部类的格式。
2.当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装。通常内部类被封装,都会被私有化,因为封装性不让其他程序直接访问。
static:如果内部类中定义了静态成员,内部类就具备static的特性,也就是该内部类必须是静态的。当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
静态内部类
在外部其他类中,如何直接访问静态内部类的非静态成员呢?
格式:new 外部类名.内部类名().内部类函数名();//这里的函数不是静态的
在外部其他类中,如何直接访问静态内部类的静态成员呢?
外部类名.内部类名().内部类函数名();
注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是static的。
内部类定义原则
1.类是用来描述现实中的事物的。当描述事物时,事物的内部还有事物,该事物就用内部类来描述。因为内部事物在使用外部事物的内容。
2.内部类只有被定义在成员位置上才能被私有化或被静态所修饰。
3.如果内部类定义在方法中,这个内部类就是局部的,它不能被私有化或被静态所修饰。
4.因为私有化或静态只是修饰成员的,现在这个类局部了就不能被修饰了。
5.局部内部类是不能定义静态成员的。
内部类定义在局部时:
1.不可以被成员修饰符修饰
2.可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问他所在的局部中的变量。只能访问被final修饰的局部变量。
匿名内部类
1.匿名内部类其实就是内部类的简写格式。
2.定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。 内部类可以继承或实现一个外部类或者接口。
3.匿名内部类的格式:new父类或者接口(){定义子类的内容}.方法;
new 外部类名或接口名(){覆盖类或者接口中的代码,也可以自定义内容。}.方法;
4.其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖,也可以理解为带内容的对象。
是把定义类和建立对象封装为一体的一个表现形式 匿名子类对象,就是建立一个带内容的外部类或者接口的子类匿名对象。
5.匿名内部类中定义的方法最好不要超过3个。最好是1个或2个。
12.Object
Object:是所有对象的直接或者间接父类,也可以说是所有对象的超类,Java认为所有的对象都具备一些基本的共性内容,这些内容可以不断的向上抽取,最终就抽取到了一个最顶层的类中的,该类中定义的肯定是所有对象都具备的功能。
Object类里的一些方法
equals():Object里的方法,指示其他某个对象是否与此对象”相等”。用于比较两个对象是否相等,其实内部比较的就是两个对象地址。
而根据对象的属性不同,判断对象是否相同的具体内容也不一样。所以在定义类时,一般都会复写equals方法,建立本类特有的判断对象是否相同的依据。
hashCode():返回该对象的哈希码值。也就是内存地址,支持此方法是为了提高哈希表的性能。
Integer.toHexString():转十六进制
getClass():返回此Object的运行时类。返回值类型是Class
toString():返回该对象的字符串表示,默认返回的格式:类名@哈希值 = getClass().getName() + ‘@’ + Integer.toHexString(hashCode())。
为了对象对应的字符串内容有意义,可以通过复写,建立该类对象自己特有的字符串表现形式。
13.abstract(抽象类)
当多个类中出现相同功能,但功能主体不同。这个时候我们可以进行向上抽取,我们抽取的只是功能定义,而不是功能主体。
什么是抽象呢?
抽象就是笼统的、模糊的、不具体的,也可以说看不懂的东西就是抽象的东西。简单来说看不懂的方法就是抽象的方法。
什么时候需要声明为抽象方法呢?
在不断抽取过程中,我们将共性内容中的方法声明抽取,但是方法不一样,没有抽取,所以这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。
记住:抽象方法所在的类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
抽象类的特点:
1.抽象方法只能定义在抽象类中,抽象类和抽象方法都必须被abstract关键字修饰(可以描述类和方法,不可以描述变量)。
2.抽象方法只定义方法声明,并不定义方法实现。
3.抽象类不可以被创建对象(实例化),也就是说抽象类不可以用new创建对象,因为调用抽象方法是没有意义的。
4.抽象类中的方法要想被使用,必须由子类复写所有的抽象方法,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类也是一个抽象类。
注意:只有通过子类继承抽象类并覆盖了抽象类中所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
抽象类中是否有构造函数?
有,用于给子类对象进行初始化。
抽象类中是否可以定义非抽象方法?
可以。其实抽象类和一般类是没有太大区别的,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上都需要定义属性和行为的。只不过比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
抽象关键字abstract和哪些不可以共存?
final、private、static
抽象类中可不可以不定义抽象方法?
可以。抽象方法目的仅仅为了不让该类创建对象。
模板方法设计模式:
在定义功能时,当功能的内部一部分实现是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。
思想:我的一部分代码需要子类去做,就把这部分暴露出去。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
abstract class GetTime//父类就是获取运行时间的,运行的程序由子类进行
{
public final void getTime()//这个方法不可以复写,所以加上final强制性避免覆盖 提高了复用性 不一定必须加final
{
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runcode();//因为不明确,所以是抽象的,因此类也是抽象的 但是这个方法不一定是抽象的有可能有默认的 要记住这个思想
}
class SubTime extends GetTime
{
public void runcode()//子类复写功能方法 提高了扩展性
{
for(int i = 1; i<10; i++)
{
System.out.println(i);
}
}
}
class TemplateDemo
{
public static void main(String[] args)
{
//GetTime gt = new GetTime();
SubTime gt = new SubTime();
gt.getTime();
}
}
14.接口(interface)
初期理解,可以认为接口是一个特殊的抽象类,在英文里的所属关系是“like a”。
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
接口定义时,格式特点:
1.接口中常见定义:全局常量,抽象方法。
2.接口中的成员都有固定修饰符。
常量:public static final
方法:public abstract
格式:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
interface Inter{
public static final int x = 3;
public abstract void show();
}
注意:定义的常量中static和final都可以省略不写,方法中的public abstract也可以省略。当你省略后系统会自动把这些给你加上,但是最好还是写上,这样可以让阅读性更强一些。 还要记住,接口中的成员都是public的。
3.接口中有抽象方法,说明接口不可以实例化。接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
4.类与类之间存在继承关系,类与接口之间存在的是实现关系。继承用extends 实现用implements
接口的特点:
1.接口是对外暴露的规则,也就是对外提供的规则。
2.接口是程序的功能扩展。如:可以理解为主板上提供的接口。
3.接口可以用来多实现,这是类与接口不一样的地方,也就是多继承改良后的结果。java将多继承机制通过多实现来体现。
4.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
5.接口与接口之间可以有继承关系。
6.主要特点是降低了耦合性。
为什么要定义接口?
在需要扩展功能的时候就定义接口。如:电脑硬件要升级的时候。
接口给我们带来了什么好处呢?
接口提高了功能的扩展性和降低了耦合性。
继承与实现的区别:
简单的说:
1,类与类之间会存在继承关系。
2,类与接口之间,接口与接口之间会存在实现关系。
3,JAVA支持单继承和多实现。
4,继承指子类继承父类中已经有的方法,相同的方法将会子类覆盖父类的方法;实现指接口中定义的方法名却没有具体的方法体,需要自己去写。
继承:必须是类与类之间有所属关系才可以继承。关键字extends
继承:是子类和父类 可以看出继承只能是单继承的 就是只能有一个父类 他们是有上下关系的 子类继承父类后可以获得父类中所用的方法。
继承提高了代码的复用性,它把共有的数据提取出来,子类只需要继承就可以了
实现:实现是接口,类与接口是实现关系关键字implements
接口可以实现多实现,即一个类可以实现多个接口。所以接口的好处是提高了功能的扩展性和降低了耦合性.
接口里只定义了功能,而没有方法的实现,实现需要别的类去实现,功能里的内容需要你自己去写。而继承父类已经把功能实现了 你继承了它就可以直接使了 你要是不一样只能覆盖。
抽象类与接口:抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取,特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。
抽象类和接口的共性:都是不断向上抽取的结果。
抽象类与接口的区别:
1.抽象类只能被继承,而且只能单继承。
接口需要被实现,还可以被多实现。
2.抽象类中可以定义非抽象方法,子类可以直接继承使用。
接口中都是抽象方法,都需要子类去实现。
3.抽象类使用的是 is a关系。
接口中使用的是like a关系。
4.抽象类的成员修饰符可以自定义。
接口中的成员修饰符是固定的。都必须是public的。
接口是怎样降低耦合性的呢?
例:在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。
15.异常
异常:就是程序在运行时出现不正常情况。也就是程序中出现的问题。
异常的由来:问题也是现实生活中一个具体的事物,可以通过java的类的形式进行描述,并封装成对象。其实,就是java对不正常情况进行描述后的对象体现。
异常的好处:
1.将问题进行封装。
2.将正常流程代码和问题处理代码相分离,方便于阅读。
异常的体系
Throwable
|–Error
|–Exception
|–RuntimeException
… …
Error:错误,出现重大问题,如:运行的类不存在或内存溢出等。一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。
Exception:异常,在运行时出现的情况,可以有针对性的处理方式,通过try catch finally 解决。
注意:Error和Exception的子类名都是以父类名作为后缀的。
无论Error或者Exception都具有一些共性内容。
比如:不正常情况的信息,引发原因等。
Throwable类是java语言中所有错误或异常的超类。
异常体系的特点:异常体系中的所有类以及建立的对象都具备可抛性。
可抛性的体现:就是这个体系中的类和对象都可以被throw和throws两个关键字所操作。
throw和throws的用法:
throw定义在函数内,用于抛出异常对象,后面跟的是异常对象。
throws定义在函数上,用于抛出异常类,后面跟的是异常类名,可以抛出多个,用逗号隔开。
通常情况:函数内容如果有throw,抛出异常对象,并没有进行处理,那么函数上一定要声明,否则编译失败。但是也有特殊情况。当函数内容有throw抛出异常对象,并未进行try处理。必须要在函数上声明,否则编译失败。
注意:RuntimeException除外,也就是说,函数内如果抛出的RuntimeException异常,函数上可以不用声明。
对于问题的划分:1.严重的问题 2.非严重的问题
对于严重的,java通过Error类进行描述,对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,java通过Exception类进行描述,对于Exception可以使用针对性的处理方式进行处理。
异常有两种:
编译时被检测异常:只要是Exception及其子类都是编译时被检测的异常。
该异常在编译时,如果没有处理(没有抛也没有try),编译失败。
该异常被标识,代表这可以被处理。
运行时异常(编译时不检测)
在编译时,不需要处理,编译器不检查。
该异常的发生,建议不处理,让程序停止。需要对代码进行修正。
注意:其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。
如果函数声明了异常,调用者需要进行处理。处理方法可以throws可以try。
定义异常处理时,什么时候定义try,什么时候定义throws呢?
功能内部如果出现异常,如果内部可以处理,就用try;
如果功能内部处理不了,就必须声明出来,让调用者处理。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
异常处理的语句:
try{需要被检测的代码}catch{处理异常的代码}finally{一定会执行的代码}
有三种结合格式:
第一个格式:
try
{
需要被检测的代码;
}
catch(异常类 变量名)
{
异常处理代码;
}
第二个格式:
try
{
需要被检测的代码;
}
catch(异常类 变量名)
{
异常处理代码;
}
finally
{
一定会执行的代码;
}
第三个格式:
try
{
需要被检测的代码;
}
finally
{
一定会执行的代码;
}
注意:
1.finally中定义的通常是关闭资源代码,因为资源必须释放。
2.finally只有一种情况不会执行就是在catch里有System.exit(0);的时候,也就是当执行到System.exit(0);时catch里可以加return和System.exit(0); 加return,finally后面的就不会执行了finally还是会执行。 System.exit(0);//系统,退出。jvm结束。
finally中存放的是一定会被执行的代码。
finally代码块:定义一定执行的代码。通常用于关闭资源。
记住一点:catch是用于处理异常。如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。
RuntimeException:一个特殊的异常子类.
Exception中有一个特殊的子类异常RuntimeException运行时异常。
如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。
之所以不用在函数声明,是因为不需要让调用者处理。当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。
对捕获到的异常对象进行常见方法操作:
catch (Exception e) { //e用于接收try检测到的异常对象。
System.out.println(“message:”+e.getMessage());//获取的是异常的信息。
System.out.println(“toString:”+e.toString());//获取的是异常的名字+异常的信息。
e.printStackTrace();//打印异常在堆栈中信息;异常名称+异常信息+异常的位置。
}
对多异常的处理
1.声明异常时,建议声明更为具体的异常。这样处理的可以更有针对性,更具体。
2.对方声明几个异常,就对应有几个catch块。不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。
建立在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义已经e.printStackTrace();也不要简单的就书写一条输出语句。
异常声明throws:方法名()throws Exception//这个的意思是在功能上通过throws的关键字声明了该功能有可能会出现问题。
在函数上声明异常好处:便于提高安全性,让调用者进行处理。不处理编译失败。
异常的注意事项:
在子父类覆盖时:
1.子类抛出的异常必须是父类异常的子类或子集。
2.如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。
异常在子父类覆盖中的体现:
1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或者该异常的子类。
2.如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。不可以超出父类。
3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。异常的产生他能让正常流程和问题处理代码相分离。这是它的好处,否则会阅读性差。
自定义异常
什么是自定义异常?
当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。自定义异常:是按照java的面向对象思想,将程序中出现的特有问题进行封装。
自定义异常时:如果该异常的发生无法在继续进行运算,就让自定义异常继承RuntimeException。
如何去自定义异常类呢?
定义一个子类继承Exception或RuntimeException,目的是为了生成对象,让该类具备可抛性和具备操作异常的共性方法。
可通过throw或者throws进行操作。
我们为什么要继承Exception?
继承Exception原因:异常体系有一个特点,因为异常类和异常对象都被抛出。
他们都具备可抛性,这个可抛性是Throwable这个体系中独有特点。
异常的转换思想:当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。
我们自己定义的异常只能手动抛出
throw new 自定义异常类名();//手动通过throw关键字抛出一个自定义异常对象
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。
要么在内部try catch处理。要么在函数上声明让调用者处理。
一般情况,函数内出现异常,函数上需要声明。
自定义异常:必须是自定义类继承Exception或RuntimeException。
如何定义异常信息呢?
因为父类中已经把异常信息的操作都完成了。所以子类只要在构造时,将异常信息传递给父类通过super语句。
那么就可以直接通过getMessage方法获取自定义的异常信息。throw new 自定义异常类名(自定义异常信息);
当要定义自定义异常的信息时,可以使用父类已经定义好的功能。将异常信息传递给父类的构造函数。
示例:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
class MyException extends Exception
{
MyException(String message)
{
super(message);
}
}
16.制作帮助文档
开始制作程序的说明书。java的说明书通过文档注释来完成。
第一步
1.
/**
这里通常是对类的描述信息 如:这是一个可以对数组进行操作的工具类,该类提供了,获取最值,排序等功能。
@author(作者) XXX
@version(版本)
*/
2.
/**
方法功能的说明
如:获取一个整型数组中的最小值。
@param(参数描述) arr 接收一个int类型的数组。
@return(运行完的结果,返回值描述) 会返回一个该数组中最小值。
*/
第二步
用另外一个工具(javadoc)把文档进行提取并形成说明书
命令格式:javadoc –d 文件夹名 –auther –version *.java
如:javadoc -d(目录) 目录名 -author -version(这两个特殊,如果加了就要写上这两个) 工具类名.java 这个类如果想声明文档必须要public修饰,这就创建了API文档。
17.package(包)
定义包用package关键字。
包的作用:
1.对类文件进行分类管理 2.给类提供多层命名空间 3.写在程序文件的第一行
4.类名的全称是 包名.类名 5.包也是一种封装形式
总结:
包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
包与包直接可以使用的权限只有两种:1.public 2.protected:只能是不同包中的子类可以使用的权限。
如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中即可。
一般在定义包名时,因为包的出现是为了区分重名的类。所以包名要尽量唯一。怎么保证唯一性呢?
可以使用url域名来进行包名称的定义,url是唯一的。
示例:package cn.itcast.pack.demo;//注意:包名的写法规范,所有字母都小写。
自动生成包: 编译命令:javac –d 位置(.当前路径) java源文件
java中的四种权限:
public protected default(默认的) private
同一个类中 ok ok ok ok
同一个包中 ok ok ok no
子类 ok ok no no
不同包中 ok no no no
import(导入)
import关键字为了简化类名的书写。
建议:不要写通配符*,需要用到包中的哪个类,就导入哪个类。
import packa.*;//这个仅仅是导入了packa当前目录下的所有的类。不包含子包。
import packa.abc.*;//导入了packa包中的子包abc下的当前的所有类。
如果导入的两个包中存在着相同名称的类。这时如果用到该类,必须在代码中指定包名。
jar:java的压缩包,主要用于存储类文件,或是配置文件等。
java的压缩包
1.方便项目的携带。
2.方便于使用,只要在classpath设置jar路径即可。
3.数据库驱动,SSH框架等都是以jar包体现的。
命令格式:jar –cf 包名.jar 包目录
解压缩:jar –xvf 包名.jar
将jar包目录列表重定向到一个文件中:jar –tf 包名.jar >c:\1.txt
创建jar包
创建一个新的jar包 jar -cf 名.jar 包名 包名
在cmd窗口里输入jar会有帮助
图例:
小知识:
类中可以不定义主函数么?
可以,主函数的存在,仅为该类是否需要独立运行,如果不需要,主函数是不用定义的。
主函数的解释:保证所在类的独立运行,是程序的入口,被jvm调用。
主函数:是一个特殊的函数(也是一个一般函数,只是被jvm识别)。作为程序的入口,可以被jvm调用。
主函数的定义:
public:代表着该函数的访问权限是最大的。
static:代表主函数随着类的加载就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。也叫字符串类型的数组(存储字符串类型元素的数组)。唯一可改动的地方就是args,args(是arguments的简化)是变量名,只要名合法就行。
主函数是固定格式的:jvm识别。
jvm在调用主函数时,传入的是new String[0];
几种常见的异常:
1.NullPointerException:空指针异常。
2. IndexOutOfBoundsException:角标越界,就是超出了范围。
它有两个子类:
1.数组角标越界:ArrayIndexOutOfBoundsException
2.字符串角标越界: StringIndexOutOfBoundsException
3.ClassCasstException:类型转换异常。
4.OutOfMemoryError:内存溢出错误。
总结:
我们在开发的时候要先看java中是不是已经提供了相对应的对象,如果没有对象就自己造一个对象,把自己要用的功能放进去,在用这个对象。所以说以后在写程序的时候要先找对象,如果没有就自己造一个对象在拿来用。
开发时,记住,属性是用于存储数据的,直接被访问,容易出现安全隐患,所以,类中的属性通常被私有化,并对外提供公共的访问方法。
这个方法一般有两个,规范写法:对于属性 xxx,可以使用setXXX(),getXXX()对其进行操作。
静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块–>构造代码块–>构造函数。
内部类一般用于程序设计上。
java认为所有的对象都具备比较性,都能比较两个对象是否相同。如果自定义类中也有比较相同的功能,没有必要重新定义。只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖。
子类的实例化过程:
子类的所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。但子类中至少会有一个构造函数会访问父类中的构造函数。
记住,千万不要为了获取其他类的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。
其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口。
当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。
函数具备多态性 如:重载和覆盖。而函数本身其实就具备多态性,这就是某一种事物有不同的具体的体现。
对象与对象 类与类 事物与事物之间除继承外还有1.聚集 2.聚合
如果把工具类任意放到一个文件夹,在cmd里就要用set classpath=.;文件的地址(点是找当前)
一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致。
如果类被public修饰,那么默认的构造函数也带public修饰符。如果类没有被public修饰,那么默认的构造函数,也没有public修饰。
面向对象的设计:谁拥有数据,谁就对外提供操作这些数据的方法。
总结就是一句话:默认构造函数的权限是随着类的变化而变化的。
——- android培训、java培训、期待与您交流! ———-