2、面向对象
1 面向对象概念
2 类与对象的关系
3 封装
4 构造函数
5 this关键字
6 static关键字
7 单例设计模式
8 继承
9 多态
2.1.1 理解面向对象
面向对象是相对面向过程而言
面向对象和面向过程都是一种思想。
面向过程是强调的是功能行为,
面向对象将功能封装进对象,强调具备了功能的对象。
面向对象是基于面向过程的。
2.1.2面向对象的特点
可以将复杂的事情简单化
将程序员从执行者转换成了指挥者
是一种符合人们思考习惯的思想
举例:比如去餐馆吃饭,调用服务员的点餐行为。
2.1.3向对象的开发、设计和特征:
开发的过程:其实就是不断的创建对象,使用对象,指挥对象做事情。
设计的过程:其实就是在管理和维护对象之间的关系。
面向对象的特征:
封装(encapsulation)
继承(inheritance)
多态(polymorphism)
2.2类与对象之间的关系:
java中描述事物通过类的形式体现,类是具体事物的抽象,概念上的定义。
类的定义:
就是对现实生活中事物的描述。如:人的身高,体重等属性,和吃饭、跑步等行为。
对象即是该类事物实实在在存在的个体。
java中用类class来描述事物也是如此:
属性:属性对应类中的成员变量
行为:行为对应类中的成员函数。
定义类其实就是定义类中的成员(成员变量和成员函数)
类和对象之间的关系,可以用制造汽车的图纸和汽车实体做比方。类就是图纸
汽车就是堆内存中的对象。
2.2.1成员变量和局部变量之间的区别:
成员变量:
成员变量定义在类中,在整个类中都可以被访问。
成员变量随着对象的建立而建立,存在于对象所在的堆内存中。
成员变量有默认初始化值。
局部变量:
局部变量只定义在局部范围内,如:函数内,语句内等。
局部变量存在于栈内存中。
作用的范围结束,变量空间会自动释放。
局部变量没有默认初始化值。
2.2.2创建对象和使用对象
class Car//对Car这类事物进行描述
{
String color="red";
int num = 4;
void show()
{
System.out.println("color="+color+"::"+"num="+num);
}
}
class CarDemo
{
public static void main(String[] args)
{
Car c = new Car(); // 建立对象
c.color = "green"; // 对对象的属性进行修改
c.show(); ///使用对象的功能
}
}
2.2.3 匿名对象
匿名对象是对象的简化形式
匿名对象两种使用情况
当对对象方法仅进行一次调用的时
匿名对象可以作为实际参数进行传递。
例: new Car().num =5; 创建一个对象;
new Car( ).color=”black”;又创建了一个新的对象上面的对象就成了垃圾。
匿名对象可以作为实际参数进行传递例:
class CarDemo
{
public static void main(String[] args)
{
// Car q =new Car();
//show(q);
show(new Car());//这个就是匿名对象作为实际参数进行传递的。
}
}
public static void show(Car c){ //用函数定义的功能
c.num =3;
c. color ="black";
c.run();
}
2.3封装(Encapsulation)
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
将变化隔离。
便于使用。
提高重用性。
提高安全性。
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其访问。
2.3.1 private(私有)关键字
private关键字:
是一个权限修饰符。
用于修饰成员(成员变量和成员函数)
被私有化的成员只在本类中有效
常用之一:
将成员变量私有化,对外提供对应的set ,get方法对其进行访问。提高对数据访问的安全性。同时也提高了代码的健壮性。
set方法就是设置,需要参数,不需要返回值。返回值类型为void
get方法得到一个返回值,需要确定返回值类型,不要参数。
注意:私有仅仅是封装的一种表现形式。
例:
package day03;
class Person
{
private int age;
public void setAge(int age) {
if (age > 0 && age < 100) {
this.age = age;
speak();
}
else
System.out.println("非法年龄");
}
public int getAge() {
return age;
}
void speak() {
System.out.println("age=" + age);
}
}
public class PersonDemo {
public static void main(String[] args) {
Person p = new Person();
// p.age=28;
p.setAge(-20);
// p.speak();// 成员变量在堆内存中有默认初始化值。
}
}
private权限修饰符,对成员变量修饰后出现的异常:
Exception in thread “main” java.lang.Error: Unresolved compilation problem:
The field Person.age is not visible
2.4构造函数
特点:
函数名与类名相同
不用定义返回值类型
不可以写return语句.
注意构造函数和一般函数的区别:
第一,写法不一样
第二,运行上不一样,构造函数是对象一建立就运行对对象进行初始化,而且构造函数只运行一次。
一般方法是给对象添加的功能,只有对象调用时才运行,而且可以多次调用。
作用:
作用是给对象进行初始化。
构造函数的小细节:
当一个类没加构造函数时,系统会默认给该类添加一个空参数的构造函数。形式:
例如:Person(){}
当类中定义了构造函数时,默认的构造函数就不存在了。
此外构造函数还有重载。
注意:
默认构造函数的特点。
多个构造函数是以重载的形式存在的。
构造函数也是类中的成员,可以被private私有化的。但是一私有化之后与之对应的对象就不可能被初始化了。
class Person
{
private String name;
private int age;
Person() //若没有加构造函数系统默认添加空参数的构造函数 Person(){} 方便对象进行初始化。
{
System.out.println("a:name="+name+"age="+age);
}
publis void setName(String n)
{
name =n;
}
public String getName()
{
return name;
}
Person(String n)
{
name = n;
System.out.println("b:name="+name+"age="+age);
}
Person(String n,int a)
{
name = n;
age = a;
System.out.println("c:name="+name+"age="+age)
}
}
public class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
Person p1 = new Person("zhangsan");
p1.setName("zhangerdan");
Person p2 = new Person("lisi",20);
}
}
需要注意的是这些对象都是在调用与之对应的构造函数。
什么时候定义构造函数呢?
当分析事物时,该事物存在就具备一些特性或行为,那么就将这这些特性和行为定义在构造函数中。
构造函数的有参无参的确定要分析一开始初始化时对象是否就具备。
2.4.1构造代码块
作用:给对象进行初始化。对象一建立就开始执行,而且优先于构造函数执行。
构造代码块和构造函数的区别:
构造代码块是给所有的对象进行统一初始化。
构造函数是给对应的对象的进行初始化。
构造代码块是对象所具有的共性内容。
{
System.out.println("VBNMVBNM");
}
2.5 this关键字
表面现象是this用来区别有局部变量和成员变量实际上由this的特点原理决定。
this的特点:代表其所在函数,所属对象的引用。
换言之:哪个对象调用this所在的函数,那么this就代表哪个对象。this实际意义上就是一个对象的引用。
什么时候使用this关键字呢?
当定义类中的功能时,该函数内部用到调用该函数的对象时,这时就用this来表示这个对象。
//比较两个人的年龄是否相同
class Person
{ private String name;
private int age;
Person(String name)
{
this.name = name;
}
Person(int age)
{
this.age =age;
}
Person(String name, int age)
{this(name)//这个就是用于构造函数之间的调用 this代表对象
//this.name = name;
this.age = age
}
public boolean compare(Person p)
{
return this.age==p.age;
}
}
class PersonDemo
{
Person p1= new Person(20);
Person p2 =new Person(25);
boolean b = p1.compare(p2);
System.out.println(b);
}
2.5.1 this关键字在构造函数间的调用:
this(name)这个只能用于构造函数之间的调用的,因为this不能用于一般函数之间的调用。
this语句用于构造函数之间的互相调用时,this语句只能定义在放在构造函数的第一行。
因为初始化要先执行。
this在构造函数之间的调用避免死循环式的调用。
2.6 static(静态)关键字
static关键字:
用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
1,随着类的加载而加载(随着类的消失而消失说明static的生命周期很长)。
2,优先于对象存在
3,被所有对象所共享
4,可以直接被类名调用 //写法是 类名.静态成员
实例变量和类变量的区别:
1,存放位置不同,
实例变量随着对象的建立存在在堆内存中,而类变量随着类的加载而存在在方法区中。
2,生命周期不同。
类变量的生命周期最长,随着类的消失而消失。实例变量随着对象的消失而消失。
public class PersonDemo3 {
private String name;//称作是成员变量也叫作实例变量。
static String country="cn"; //称作是静态变量。也称作是类变量。
public void setName(String name)
{
this.name=name;
}
public void show()
{
System.out.println("name="+name+":::::"+"country="+country);
}
}
public class StaticPersonDemo {
public static void main(String[] args) {
//PersonDemo3 p = new PersonDemo3();
// p.setName("zhangsan");
// p.show();
System.out.println(PersonDemo3.country);//在这被类名直接调用。
}
}
使用注意:
静态方法只能访问静态成员(理解非静态成员只有对象建立时才被建立所以只能访问静态)
静态方法中不可以写this,super关键字(因为静态优先于对象存在)
主函数是静态的。
非静态方法既可以访问静态又可以访问非静态。
静态的利弊:
利:对对象的共享数据进行单独空间的存储,节省空间,没必要每一个对象都储存一份,另外可以直接用类名调用。
弊:生命周期过长,
访问有限制。(说明静态虽好但是只能访问静态)
静态什么时候用?
分为:(成员分为成员变量和成员函数)
什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,(数值都一样比如中国同一个国籍)该数据被静态修饰。对象中的特有数据要定义成非静态的储存在堆内存中。
什么时候定义静态函数呢?
当功能内部没有访问到非静态的数据(对象的特有数据)那么该功能可以定义成静态的。
也即是功能没有操作对象的特有数据。
class Person{
String name;
public static void show()
{
System.out.println("nihao");
}
}
class PersonTest{
Person.show();//show 方法中没有设计到对象的特有数据,比如name.;类名.静态函数名
}
2.6.1静态代码块:
格式:
static
{
}
特点:随着类的加载而加载,而且只执行一次。优先于主函数执行。
应用是给类进行初始化。
注意静态代码块的执行顺序:
class StaticCode {
{
System.out.println("d");
}
static {
System.out.println("a");
}
}
public class StaticCodeTest {
static {
System.out.println("b");
}
{
System.out.println("t");
}
public static void main(String[] args) {
new StaticCode();
new StaticCode();
System.out.println("over");
}
static {
System.out.println("c");
}
}
执行完的结果是:
**b c a d d over**
说明静态代码块优先于主函数执行,并且随着类的加载而加载只执行一次,非静态代码块是为对象进行初始化的话,每调用一次执行一次。非静态构造代码块优先于构造函数执行。
2、8面向对象的第二种特性:继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
多个类可以称为子类,单独这个类称为父类或者超类。
子类可以直接访问父类中的非私有的属性和行为。
通过 extends 关键字让类与类之间产生继承关系。
继承的出现提高了代码的复用性。
继承的出现让类与类之间产生了关系,为多态的出现提供了前提。这种关系是 is a 。
2.8.2 继承的特点
Java只支持单继承,不支持多继承。(换言之一个类只能有一个父类,不可以有多个父类。)
原因是:当多个父类定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。容易造成安全隐患。
eg、class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2…//error
Java支持多层继承(继承体系)
class A{};
class B extends A {};
class C extends B{};
定义继承需要注意:
不要仅为了获取其他类中某个功能而去继承。
类与类之间要有所属( ” is a ” )关系,xx1是xx2的一种。
2.8.3子父类出现后变量的特点:
子类继承父类的变量:
class Fu {
int num1 = 5;
}
class Zi extends Fu {
int num2 = 4;
}
public class ExtendsDemo {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z.num1 + "::::::" + z.num2);//结果是 5:::4
}
}
如果子父类中出非私有同名变量时子类对象执行的是子类自己的变量this
子类要访问父类的同名变量用super关键字。
如下:
class Fu {
int num = 5;
}
class Zi extends Fu {
int num = 4;
}
public class ExtendsDemo {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z.num + "::::::" + z.num);//执行结果是:4::::4
}
}
2.8.3.1 super关键字
用法和this很像。
区别:
this代表的是本类对象的引用
super代表的是父类对象的引用。
补充:
super代表父类的内存空间的标识。
当子父类出现同名成员时,可以用super进行区分
子类要调用父类构造函数时,可以使用super语句。
class Fu {
int num = 5;
}
class Zi extends Fu {
void show ()
{
System.out.println(super.num);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Zi z = new Zi();
z.show(); //结果是 5 父类引用指向子类对象。
}
2.8.4函数覆盖(Override)
啥是函数的覆盖呢?
子类中出现与父类一模一样的函数时,子类对象在调用该函数时运行子类中的函数。
像是父类中的方法被覆盖了一样。
函数的重载overload和函数的重写override
函数的重载 :(只需要看参数列表 函数名相同)
在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
函数的覆盖或重写 子父类中的方法需要一模一样(包括返回值类型)
函数的覆盖需要注意的:
父类中的私有方法不可以被覆盖。
在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
class Tel
{ void show()
{
System.out.println("number");
}
}
class NewTel
{
void show
{
super.show(); //在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
System.out.println("name");
System.out.println("pic")
}
}
另外覆盖还需要注意的是:
覆盖时,子类方法权限一定要大于等于父类方法权限。(特别是在接口时,抽象方法都是public 的所以子类要覆盖父类的方法只能也是public)
静态只能覆盖静态。
覆盖的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子l类特有的内容。
2.8.5子类的实例化过程:
子父类中的构造函数
构造函数的作用就是给对象进行实例化。
this()代表的是调用的本类的构造函数。
super()代表的是调用的父类的构造函数。都必须放在构造函数的第一行进行初始化故不能同时放在一起。
子类构造函数的第一行可以写this();来访问本类中的构造函数。子类中至少有一个构造函数会访问父类中的构造函数。
注意:
子类中所有的构造函数默认都会访问父类中空参数的构造函数
因为每一个子类构造函数的第一行都有一条默认的隐式语句super();
class Fu05{
Fu05(){
System.out.println("父类的构造函数");
}
}
class Zi05 extends Fu05
{
Zi05(){ //这里有一条隐式语句 super();
System.out.println("子类的构造函数");
}
}
public class ExtendsDemo3 {
public static void main(String[] args)
{
Zi05 z = new Zi05(); //执行结果是:父类的构造函数 子类的构造函数
}
}
子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。
注意:
当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
为什么子类一定要访问父类 中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立的时候会首先查看下父类中的数据是怎么初始化的。
所以子类对象在初始化时会首先访问父类中的构造函数。
—————————————————————————————————————————————————————
以上是关于面向对象的封装、和继承
—————————————————————————————————————————————————————
3.1final关键字
final 最终 可以修饰类,方法,变量。
final修饰的类不可以被继承。
final修饰的方法不可以被覆盖。
被final修饰的变量是一个常量只能赋值一次。final既可以修饰成员变量又可以修饰局部变量。
内部类只能访问被final修饰的局部变量。
当在描述事物时,一些数据是固定的为了增强阅读性,给这些值起个名字,方便阅读。而这个值不需要改变,所以加个final修饰
作为常量书写时要大写,多个单词组成时单词之间加个下划线_。
class Demo
{
public static final double MY_PI = 3.14; //全局常量
final void show()
{
System.out.println("a");
}
}
3.2抽象类
抽象定义:
抽象就是从多个事物中将共性的,本质的内容抽取出来。
例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。
抽象类:(包含抽象方法的类)
java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法的由来:
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
抽象类的特点:
抽象类和抽象方法必须用abstract关键字来修饰。只能修饰类和方法。
抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式为:修饰符 abstract 返回值类型 函数名(参数列表) ;
class Demo{
public abstract void show();
}
abstract class Student
{
abstract void study();
}
注意:
1,抽象类不可以被实例化,也就是不可以用new创建对象。//Cannot instantiate(示例) the type StudentDemo1
原因如下:
抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。
2,抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
3、抽象类中必须有抽象方法,也可以有非抽象的方法。抽象类和一般的类没有什么太大的不同只是里面定义了一些无法定义主题功能的方法。
abstract class StudentDemo1
{
abstract void study();
}
public class BaseStudentDemo extends StudentDemo1
{
void study(){
System.out.println("base study");
}
}
public class BaseStudentDemo extends StudentDemo1
{
void study(){
System.out.println("base study");
}
}
public class AbstractDemo
{
public static void main(String[] args) {
new BaseStudentDemo().study(); //子类将其抽象方法全部复写后 子类才可以被实例化 结果: base study
}
}
抽象类中可以有构造函数吗?
抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。
在继承了抽象类的子类中通过super(参数列表)调用抽象类中的构造方法
抽象类中可不可以没有抽象方法?
可以,只是将类抽象不让该类进行实例化而已。
注意:不可以和抽象abstract关键字共存的关键字
1).private :因为一个abstract方法需要被重写,所以不能修饰为private;
2).final:因为一个abstract方法需要被重写。被final修饰的方法是不能被重写的,所以不能同final共存;
3).static:因为一个abstract方法没有方法体。静态方法需要对方法体执行内容分配空间,所以不能同static共存;
3.3 接口
class用于定义类
interface用于定义接口
格式:
interface{};
接口中的成员修饰符是固定的。
成员常量:public static final
成员函数:public abstract
发现接口中的成员都是public修饰的。
类与类之间是继承关系extends,类与接口之间是实现关系 implements。
继承:父类中有非抽象方法可以直接被继承后调用。
实现:接口中的方法都是抽象的需要子类全部实现后才可以实例化。
接口是不可以创建对象的,因为里面的方法是抽象的。需要被子类实现,子类把接口中的方法全部覆盖(复写)后,子类才可以被实例化。否则,子类也是个抽象类。
java的运行文件.class文件。interface接口被javac编译之后也是.class文件。
覆盖时,子类方法权限一定要大于等于父类方法权限。
public interface InterDemo
{
public static final int NUM = 5;
public abstract void show();
}
public class TestDemo implements InterDemo
{
public void show(){}
}
public class InterfaceTest
{ public static void main(String[] args)
{
TestDemo td = new TestDemo();
System.out.println(td.NUM);
System.out.println(TestDemo.NUM);
System.out.println(InterDemo.NUM);
}
}
接口可以被类多实现。也是java不支持多继承的转换形式。
java的多实现
public interface InterDemo
{
public static final int NUM = 5;
public abstract void show();
}
public interface InterA
{
public abstract void method();//抽象方法的格式
}
public class Demo
{
public void funcation(){}
}
public class Test extends Demo implements InterDemo, InterA //java类既可以继承的同时实现多实现。
{
public void show(){}
public void method(){}
}
类与类之间的关系:继承 extends
类与接口之间的关系:实现 implements
接口与接口之间的关系: 继承 extends
多继承时存在方法体容易产生安全隐患;而多实现时由于接口中没有方法体可以由子类自由定义。
java中类与类之间只能单继承,接口与接口之间可以多继承。
3.3.1接口的特点:
接口是对外暴露的规则。
接口是程序的功能扩展。
接口的出现降低耦合性。
接口可以用来多实现。
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系。
3.3.2接口和抽象类
共性:
都是不断抽取出来的抽象的概念
区别:
1)、抽象类体现继承关系,一个类只能单继承
接口体现实现关系,一个类可以多实现
2)、抽象类是继承,是 “is a “关系
接口是实现,是 “like a”关系
3)、抽象类中可以定义非抽象方法,供子类直接使用
接口的方法都是抽象,接口中的成员都有固定修饰符。