------- android培训、java培训、期待与您交流! ----------
java基础之面向对象
面向对象是一种常见的程序结构设计方法。
面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的基础。
面向对象是一个很抽象的概念,它相对面向过程而言。
过程与对象都是一种解决问题的思想。
面向过程:强调的是功能行为,一种过程,先干啥,再干啥;
面向对象:将功能封装到对象里,强调的是具备某功能的对象;
按照面向对象的思想,可以把任何的东西看做对象!
用java语言写程序,其实就是找对象用,没有合适对象就创建对象。过程就是,找对象,建对象,用对象,维护对象间关系。
1.类和对象的关系:
类是对现实生活中某类型事物的描述,描述这类事物应该有什么共性(属性和行为),对象是这类事物的一个具体实例,可以拥有描述中所有共性的具体内容(属性值和可以执行的方法,方法被调用时才运行)。
2.想要使用对象,必须先创建对象:
从类中new出来一个对象放到堆内存中,如Person p = new Person( );过程:在堆内存中开辟个内存空间存放对象实体,封装了对象所属类的所有属性(属性值看具体初始化的方式),对象的内存地址让一个变量p存储,p是在栈内存中。p中存储的是对象实体的地址值,p指向对象实体,我们说,p是对象的引用,使用p可以操作对象的所有属性和方法。对象一旦创建就会一直存在,除非没有引用指向它了,那么,它随时可能被java垃圾回收机制回收,即释放对象所使用的内存。而引用可以接收其他对象地址值,重新指向其他对象,当它不再指向任何对象时,就会被释放。
3.成员变量和局部变量
在内存中存储区域不同:成员变量在栈内存中,因对象存在而存在。局部变量在栈内存中。
作用范围不同:成员变量定义在类中作用于整个类,局部变量定义在方法或语句中,作用于方法体内或语句块中(所在大括号内)
4.匿名对象
匿名对象就是没有名字(没有引用指向)的对象
有两种使用场景:
第一种,对对象方法(访问属性没意义)只进行一次调用;
第二种,作为对象方法的实际参数进行传递;参数名(栈中)指向对象(堆中),方法结束,对象成垃圾;
5.封装
java语言的特性之一,指的是隐藏对象的属性和实现细节,只对外提供公共访问方式
好处:将变化隔离,便于使用;提供复用性和安全性。
封装原则:把不需要对外提供的内容都隐藏起来(类,方法,对象都是封装体),把对象的属性都隐藏(私有化),仅对外提供公共访问方式。
注意:java提供了很多现成的类供我们使用,只知道如何使用就行,不需要知道其内部原理,但为了以后我们自己创建类,还是要知道其内部原理的。
私有是封装的多种表现形式中的一种,通过权限修饰符private实现,private修饰成员(成员变量和成员方法),把成员封装到类中,对外提供访问方式,对数据访问进行控制,提高代码健壮性。
6.构造函数
用来初始化对象的函数
特征:与类同名,没有返回值(没有void,也没有return)
每个类中都有一个默认空参数的构造函数,如果自己定义了构造函数,那么系统就不再提供默认的空参数构造函数了。
一个类中可以定义多个构造函数,但是参数列表必须不同,这些构造函数构成了函数的重载。重载,是指在一个类中有多个同名函数存在,但是参数列表不同(参数类型,参数个数)。
构造代码块,就是类中用大括号括起来的一个代码块,用来初始化所有对象。对象一旦创建,先执行构造代码块初始化,后执行构造函数初始化。
7.this关键字
具体应用场景:
一、定义类中功能时,本类中函数用到调用该函数的对象时,this指代该对象的引用:例如,构造函数中成员变量和局部变量(参数)同名
二、this语句可以用来进行构造函数间调用,必须放在构造函数中的第一行
10.static关键字
static是静态修饰符,修饰成员(成员变量和成员方法)。
当类中所有的对象拥有相同的数据时,这样的数据没必要在对象都单独存储一份,用static修饰后,会在方法区(数据区,共享区)存储一份,供对象使用。
被static修饰的成员特点:
一、随着类的加载而加载,随着类的消失而消失。生命周期很长。
二、优先于对象存在
三、被所有对象共享
四、可以直接被类名调用
被static修饰的静态成员变量称为类变量,非静态成员变量称为实例变量,区别:
1)、存放位置不同:
静态变量随着类的加载而存在于方法区
实例变量随着对象的创建而存在于堆内存中
2)、生命周期不同:
静态变量随着类的加载而加载,类的消失而消失,生命周期很长。
实例变量随着对象的创建而存在,随着对象的消失而消失。
静态使用注意:
1)、静态只能访问静态,非静态既可以访问静态也可以访问非静态
2)、静态方法中不能有this、super关键字,因为静态方法存在时还没有对象
3)、主函数是静态的
(其实也就三点:加载顺序(静态随类加载时对象还没存在,所以静态只能访问静态),存放位置(决定了生命周期),生命周期)
怎样使用静态?
因为静态可以修饰成员变量和成员方法,所以从两个方面下手:
什么时候定义静态变量呢?
当类中对象有共享的数据时,这些数据定义为静态,具体对象拥有的特有数据定义为非静态
什么时候定义静态方法呢?
当方法中没有访问到非静态数据(对象的特有数据)时定义为静态方法
例如,工具类,类中的方法一般只接收调用者传递的数据,没有类就实例的特有数据,所以定义为静态方法,不需要创建对象,直接类名调用即可。工具类中有很多方法,而有些方法是为类中其他方法使用的,不需要对外提供访问方式,就私有化。
11.对象初始化的过程:Person p = new Person( )
1)、将Person.class加载进内存,因为new Person()用到Person.class。
2)、执行静态代码块,初始化类
3)、在堆内存中开辟空间,分配内存地址
4)、在分配的内存中建立对象特有属性,并默认初始化
5)、对对象特有属性显示初始化
6)、构造代码块初始化
7)、构造函数初始化
8)、将内存地址赋给栈内存中的变量p
12.对象调用方法的过程
jvm虚拟机调用main方法,main方法进栈
对象或类调用方法,在栈中开辟内存空间,方法从方法区进栈
方法执行完毕,内存释放
方法没被调用时就在方法区呆着,被调用时才进栈执行,执行完就从栈中消失。
13.单例设计模式
就是让一个类在内存中只能存在一个对象
思路:不允许类外创建对象,类中创建对象并对外提供对象获取方法
私有化构造方法,创建对象(或引用),对外提供获取对象方法。
//懒汉式单例设计模式
public class Single {
public static void main(String[] args) {
}
static private Single single = null;
private Single(){
}
public static Single getInstance(){
if(single==null)
synchronized(Single.class){
if(single == null)
single = new Single();
}
return single;
}
}
注意:单例只是保证对象在内存中的唯一性,类该怎么描述还怎么描述,只是加上三步
14.继承
概述:现实中,一些类之间存在一些相同的描述,把这些相同的描述抽取出来封装成类,那么之前的那些类和这个类就有了关系--继承。java中是先有父类,后有子类。
单继承:一个类只能继承一个父类。这样是为了降低类与类之间关系的复杂性
多层继承:父类可以派生子类,子类还可以有自己的子类,以此类推,就构成了一个继承体系。父类定义的是基本的共性内容,子类除了有父类的共性内容还有自己特有的内容。所以,使用继承体系时先了解父类再了解子类。
子父类中成员(变量、方法和构造方法)的关系:
1)、成员变量
子类继承了父类所有的非私有成员变量,子类对象可直接使用。当子类中成员变量和父类中非私有成员变量名相同时,想使用父类的变量用关键字super,想使用子类的变量用关键字this。
super指代父类对象的引用,this指代子类对象的引用
2)、成员方法
子类继承了父类所有的非私有成员方法,子类对象可以直接调用。如果子类中定义了和父类中相同的方法(同名同参数列表同返回值类型),那么子类对象调用的是子类的方法,因为子类方法覆盖(重写)了父类方法。
重写:子父类方法的功能相同,实现内容不同
子类方法重写父类方法,子类方法权限大于等于父类方法权限
静态只能重写静态
3)、构造方法
子类不能继承父类的构造方法,但当子类创建对象并初始化时必须先访问父类的构造方法,因为所有的子类构造方法中第一行有个默认的父类空参数构造方法:super()。
为什么呢?
因为子类对象可以直接获取父类的数据,那么子类对象初始化时应该先执行父类对数据的初始化,才能获取父类的数据。
如果父类中没有了默认的空参数构造函数,那么子类构造函数中必须在第一行指定执行的父类构造函数
子类的构造函数中第一行也可以使用this语句访问本类的构造函数
此时this语句和super语句不能同时出现
15.final关键字
1)、final作为修饰符可以修饰类、函数和变量
2)、final修饰类时,这个类是最终类,不允许被继承
3)、final修饰函数时,这个函数不允许被复写
4)、final可以修饰成员变量和局部变量,被final修饰的变量是常量,只能被赋值一次,不允许修改。作为常量,符合常量书写规范
16.抽象类
含有抽象方法的类就是抽象类
抽象方法:当多个类中有相同的功能定义,但功能主体不同,这时可以将相同的功能定义抽取,但不定义功能主体,那么这个方法就是抽象方法,用abstract修饰
1)、抽象方法必须放在抽象类中,都用abstract修饰
2)、抽象类不可以new对象,因为对象调用抽象方法没意义
3)、要想使用抽象类中的方法,必须由其子类复写全部抽象方法,由子类对象调用
抽象类和一般类的区别不过是多了抽象方法,不能创建对象罢了,该怎么描述还怎么描述
17.模板方法设计模式
当定义一个功能时,有些代码是确定的,有些代码是不确定的,而确定的代码在使用不确定的代码,那么就将不确定的代码暴露出去,让它的子类去完成。
/*
* 需求:获取一段程序运行的时间。 原理:获取程序开始和结束的时间并相减即可。
* 获取时间:System.currentTimeMillis();
*/
abstract class GetTime {
public final void getTime() {
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 x = 0; x < 4000; x++) {
System.out.print(x);
}
}
}
class TemplateDemo {
public static void main(String[] args) {
// GetTime gt = new GetTime();
SubTime gt = new SubTime();
gt.getTime();
}
}
18.接口
初期理解:有抽象方法的类是抽象类,当类中的方法都是抽象方法时就成了接口了。
1)、接口中常定义常量和抽象函数
2)、常量和函数有默认固定的修饰符:
public static final int NUM=9;
public abstract void method();
1)、接口不能创建对象
2)、接口需要被子类实现其全部抽象方法才能创建子类对象,否则子类是一个抽象类
3)、java支持接口多实现:一个类可以实现多个接口
一个类不可以继承多个类,是因为多个类中的方法可能会有重复,子类不知道调用哪一个。而接口中的方法都没有方法体,全部需要子类实现,不会有冲突。
1)、接口之间支持多继承
2)、接口可以看作类的继承体系的一种功能扩展或补充
19.多态
可以理解为事物的多种体现形态。比如,动物:猫、狗,一个具体的猫可以叫他猫,也可以叫他动物。
1)、多态的体现:
父类引用指向了子类对象
父类引用也可以接受本类对象
2)、多态的前提:
类与类直接必须有关系,要么继承,要么实现
通常还存在覆盖
3)、多态的好处:
大大提高了程序的扩展性
4)、多态的弊端:
父类的引用虽然指向子类对象,但只能访问父类的内容
5)、如何使用子类特有方法:
让父类的引用指向子类对象,是为了方便访问父类方法,与具体对象无关,无法访问子类对象的特有方法。如果想访问子类对象的特有方法,必须将父类的引用强制转型为子类类型。强制转型时先判断父类引用指向的对象是不是子类的实例,因为如果父类引用指向的是父类的对象的话是不能强制转型的。
6)、多态的应用:
父类中有一些方法,子类继承了也可能复写了父类的方法,子类会有很多个,当子类对象使用父类定义的功能时,先创建本类对象,再调用父类的或被子类复写了的方法,多个子类时,需要创建多个对象,扩展性差。可以这样,将父类中定义的功能封装成工具类,功能方法的形参为父类型,这样可以接收父类所有子类的对象,子类对象就可以调用父类中定义的子类中的具体方法了。
20.Object类
java中所有类的直接或间接父类,也就是java的根类。传说中的上帝。
Object类中定义的方法是所有类共有的最基础的方法,例如,equals方法,toString方法等,子类使用时可以根据自己的需要进行复写。
21.内部类
定义在类里面的类
内部类可以直接访问外部类的成员
外部类访问内部类成员,必须先建立内部类对象:
Out.In in = new Out().new In();
in.内部类成员名
/*
内部类的访问规则:
1,内部类可以直接访问外部类中的成员,包括私有。
之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。
*/
class Outer{
private int x = 3;
class Inner{
//int x = 4;
void function()
{
//int x = 6;
System.out.println("innner :"+Outer.this.x);
}
}
void method()
{
Inner in = new Inner();
in.function();
}
}
class InnerClassDemo{
public static void main(String[] args) {
Outer out = new Outer();
out.method();
//直接访问内部类中的成员。
// Outer.Inner in = new Outer().new Inner();
// in.function();
}
}
匿名内部类:
/*
匿名内部类:
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
*/
abstract class AbsDemo{
abstract void show();
}
class Outer{
int x = 3;
class Inner extends AbsDemo{
int num = 90;
void show()
{
System.out.println("show :"+num);
}
void abc()
{
System.out.println("hehe");
}
}
public void function(){
//AbsDemo a = new Inner();
// Inner in = new Inner();
// in.show();
// in.abc();
AbsDemo d = new AbsDemo(){
int num = 9;
void show(){
System.out.println("num==="+num);
}
void abc(){
System.out.println("haha");
}
};
d.show();
//d.abc();//编译失败;
}
}
class InnerClassDemo{
public static void main(String[] args) {
new Outer().function();
}
}