黑马程序员——面向对象
来源:互联网 发布:记事本界面的java代码 编辑:程序博客网 时间:2024/05/02 14:46
——- android培训、java培训、期待与您交流! ———-
什么是面向对象?
例如要买一个电脑,因为我不懂怎么买,不知道怎么看电脑的硬件和不知道怎么砍价。那么我可以找一个懂买电脑的人,
我带他一块去买电脑,我不需要和别人砍价,也不需要知道电脑的配置怎么样才算好,只要交给懂买电脑的人帮我完成这些工作就行。
那么,懂买电脑的人就是一个对象,而我,只是在调用那个人的功能。我不需要知道他是怎么砍价,怎么知道电脑配置好不好。
我只要知道他具备这些功能,而我只需要去调用他的功能就行。
面向对象举例:
1,人们可以通过电脑视频或者聊天,然而,我们不需要知道电脑是如何实现此功能的,我们只需要知道如何去使用电脑,如何去调用电脑的功能就能够达到我们的目的,那么电脑就是一个对象,而我们只是在调用电脑这个对象的功能
2,风扇能旋转扇风,我们不需要知道风扇是如何实现旋转的功能,只需要知道,我们只要打开一个开关,风扇就能转动,转动这个功能属于风扇,而我们只是在调用风扇的开关,那么风扇就相当于一个对象。
面向对象三个特征:封装、继承、多态
封装:
简单的讲,封装就是将复杂的事物封装起来,不让别人看见,然后提供一个对应的访问方式。之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断语句等。 对访问的数据进行操作,提高代码健壮性
代码演示:
class Person { private int age;//将年龄进行封装,设置为私有的 public void setAge(int age) {//对外提供能够设置年龄的方法 //在访问方式中加入逻辑判断语句 if (age > 0 && age < 130) {//如果年龄在0-130之间,那么,视为合法年龄 this.age = age; speak(); } else//年龄不合法则输出非法年龄 System.out.println("illegal age"); } public int getAge() {//提供能够获取年龄的方法 return age; } public void speak() { System.out.println("age=" + age); }}public class Demo { public static void main(String[] args) { Person p = new Person(); p.setAge(20);//age=20 // p.setAge(-40);//结果:illegal age }}
继承:
1.提高了代码的复用性
2.让类与类之前产生了关系。有了这个关系,才有了多态的特性。
(注意:注意:千万不要为了获取其他类的功能,简化代码而继承)
代码演示:
class A{ void demo1(){} void demo2(){} } //B为了少写demo1而继承A这种方式是错的,因为B它不仅继承了demo1,同时还继承了demo2,B要继承A,则必须将A中的demo1和demo2都继承 class B extends A{ //void demo1(){} void demo3(){} } //正确的继承方式如下: class A{ void demo1(){} } class B extends A{ //void demo1(){} void demo2(){} } //这是正确的继承方式,B为了省略demo1而继承A中的demo1,C为了省略demo1而继承了A中的demo1。 //B继承了A的全部,C也继承了A的全部,所以正确 class c extends A{ //void demo1(){} void demo3(){} }
多态:
可以理解为事物存在的多种体现形态。
1.多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2.多态的前提
必须是类与类之前有关系。
通常还有一个前提:存在覆盖。
3.多态的好处
多态的出现大大提高了程序的扩展性。
4.多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
代码示例:
abstract class DongWu{ //动物类 public abstract void chiFan(); //吃饭方法}class Cat extends DongWu{ //猫类继承动物类 public void chiFan(){ System.out.println("吃鱼"); } public void zhuaLaoShu(){ //抓老鼠方法 System.out.println("抓老鼠"); }}class Dog extends DongWu{ //狗继承动物类 public void chiFan(){ System.out.println("吃骨头"); } public void kanJia(){ //看家方法 System.out.println("看家"); }}public class Demo{ public static void main(String[] args){ DongWu d = new Cat(); //向上转型 function(d); function(new Dog()); /* 结果: 吃鱼 抓老鼠 吃骨头 看家 */ } public static void function(DongWu d){ d.chiFan(); //此处只能调用父类中的成员 //d.zhuaLaoShu(); //这是错误的,因为父类中没有该方法 //d.kanJia(); //这是错误的,因为父类中没有该方法 if(d instanceof Cat){ //判断传给d的对象是不是Cat Cat c = (Cat)d; //向下转型 c.zhuaLaoShu(); } else if(d instanceof Dog){ //判断传给d的对象是不是Dog Dog dog = (Dog)d; //向下转型 dog.kanJia(); } }}
构造代码块:
作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数执行。
构造代码块和构造函数的区别:
构造代码块:是给所有对象进行统一初始化
构造函数:是给对应的对象进行初始化
构造代码块中定义的是不同对象共性的初始化内容,
例如:
每一个孩子出生时都会哭,那么可以吧哭这个功能放在构造代码块中,
因为哭是每一个孩子都具备的共性内容
代码示例:
/*创建一个人对象,使得该对象一出生时,就会哭一次*/class Person{ public String name; public int age; //由于每一个孩子一出生都具备"哭"的功能, //所以可以把"哭"这个功能定义在构造代码块里,这样, //每当创建一个对象,都会"哭"一次 { System.out.println("哭"); } //创建一个对象,接收一个name 和 age public Person(String name,int age){ this.name = name; this.age = age; System.out.println("name="+name+"...age"+age); }}public class Demo { public static void main(String[] args) throws Exception { new Person("zhangsan",0);//创建第一个对象,一创建出来就会哭 new Person("lisi",0);///创建第二个对象,一创建出来就会哭 /* 结果: 哭 name=zhangsan...age0 哭 name=lisi...age0 */ }}
/* 证明构造代码块的执行优先于构造函数 */class Person { int age; String name; // 一旦创建一个对象时,此语句会优先执行,并且优先级高于构造函数 { System.out.println("构造代码块运行"); } public Person() { System.out.println("构造方法Person()运行"); } public Person(String name) { System.out.println("构造方法Person(String name)运行"); }}public class Demo { public static void main(String[] args) { new Person();//创建一个无参构造函数 new Person("zhangsan");//创建一个参数为字符串的构造函数 /* 结果 构造代码块运行 构造方法Person()运行 构造代码块运行 构造方法Person(String name)运行 */ }}
this:
用于区分局部变量和成员变量同名情况。
this为什么可以解决这个问题?
this到底代表什么呢?
this:就是代表本类的对象,到底代表哪一个呢?
this代表它所在函数所属对象的引用。
简单说:哪个对象在调用this所在的函数,this就代表哪个对象。
代码演示:
/* this代码示例 */class Person { String name; int age; public Person(String name){ this.name = name; } public Person(String name, int age) { this(name);//相当于p(name) --> new Person(name)而且此方法必须放在开头出 this.age = age;// 当p1调用此构造方法时,相当于p1.age = age System.out.println(name + "..." + age);// 因为是p1在调用,所以打印的结果是p1中的name和age } public void setName(String name) { this.name = name; System.out.println("name=" + name); } public void setAge(int age) { this.age = age; System.out.println("age=" + age); } // 需求:给人定义一个用于比较年龄是否相同的功能,也就是是否是同龄人 // this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这是用this来表示这个对象。 // 但凡本类功能内部使用到了本类对象,都使用this表示 public boolean compare(Person p2) { // 因为是p1在调用此方法,所以this相当于p1,那么相当于p1.age == p2.age return this.age == p2.age; }}public class Demo { public static void main(String[] args) { Person p1 = new Person("zhangsan", 25);// p1在调用Person的构造方法,那么构造方法中的this相当于p1 p1.setName("ZHANGSAN");// p1在调用Person中的setName方法,那么setName中的this相当于p1 Person p2 = new Person("wangwu", 22); p2.setName("WANGWU");// 因为p2在调用setName方法,那么setName方法中的this相当于p2 boolean b = p1.compare(p2); System.out.println(b); /* 结果: zhangsan...25 name=ZHANGSAN wangwu...22 name=WANGWU false */ }}
super
子类要访问父类中的同名变量,用super
super和this的区别:
this代表的是本类对象的引用
super代表的是父类对象的引用
代码示例:
class Fu{ int num = 4;}class Zi extends Fu{ int num = 0; public void show(){ //super关键字代表的是父类的引用,所以super.num使用的是父类中的num //this代表本类对象,所以this.num输出的是本类中的num System.out.println("子类的num="+this.num+",父类的num="+super.num); }}public class Demo { public static void main(String[] args) throws Exception { new Zi().show(); //结果:子类的num=0,父类的num=4 }}
static
用法:是一个修饰符,用于修饰成员(成员变量,成员函数)
注意:不能修饰局部变量
当成员被静态修饰后,就多了个调用方式,除了可以被对象调用外,还可以直接被类名调用。类名.静态成员
静态的数据存在于方法区中
static特点:
1.随着类的加载而加载。
也就是说:静态会随着类的消失而消失。说明它的生命周期最长。
2.优先于对象存在
明确一点:静态是先存在,对象是后存在的。
3.被所有对象共享
4.可以直接被类名调用
实例变量(成员变量)和类变量(既被static修饰的变量)区别:
1.存放位置:
类变量随着类的加载而存在于方法区中。
实例变量随着对象的建立而存在于堆内存中。
2.生命周期:
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失
静态使用注意事项:
1.静态方法只能访问静态成员。
非静态方法既可以访问静态也可以访问非静态。
2.静态方法中不可以定义this,super关键字。
因为静态方法优先于对象存在。所以静态方法中不可以出现this。
静态有利有弊
利处:1.对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份
2.可以直接被类名调用。
弊端:1.生命周期过长。
2.访问出现局限性。(静态虽好,但只能访问静态)
静态变量什么时候使用:当对象中出现共享数据时,该数据被静态修饰
静态方法什么时候使用:当方法没有访问到非静态数据(对象的特有数据)
那么该方法可以定义为静态的
注意:局部变量不可以设置为静态的(例如:在方法中不可以定义static变量,因为在方法中的数据都是局部的)
静态代码块
特点:随着类的加载而执行,优先于主函数执行,而且只执行一次。因为类加载进内存只加载一次
作用:给类进行初始化
代码示例:
class Demo1 { static{ System.out.println("a"); }}public class Demo { static{ System.out.println("b"); } public static void main(String[] args) { new Demo1(); new Demo1(); System.out.println("over"); } static{ System.out.println("c"); }}//执行结果: b c a over
面试中的重点:懒汉式与饿汉式
(面试专用题,必考题,不懂就背下来)
注意:懒汉式和饿汉式有什么不同?懒汉式的特点在于延迟加载,饿汉式不延迟加载。
懒汉式的延迟加载有没有问题?有,如果多线程访问时会出现安全问题。
怎么解决?可以加同步来解决,用同步代码块和同步方法都行,但是稍微有些低效。
用双重判断方式可以解决这个效率问题。
加同步时候,使用的锁是哪一个?该类所属的字节码文件对象。
饿汉式:Single类一进内存,就已经创建好了Single对象。定义单例建议使用饿汉式
class Single{ private static final Single s = new Single();//一进内存就创建好了对象 private Single(){} public static Single getSingle(){ return s }}
懒汉式: Single类进内存,对象还没有存在,只有调用了getSingle方法时,才建立Single对象
class Single{ private static Single s = null;//进内存时,还没有对象 private Single(){} public static Single getSingle(){ if (s == null) { synchronized (Single.class) { if (s == null) { s = new Single();//调用了getSingle方法时,才建立Single对象 } } } return s; }}
final:
1.可以修饰类,函数,变量
2.被final修饰的类不可以被继承
3.被final修饰的方法不可以被覆盖,但是可以重载
4.被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员变量,也可以修饰局部变量
(注意:static只能修饰成员变量)
5.书写规范:所有字母都大写,如果由多个单词组成,那么就通过 _ 连接
覆盖:
当子类出现和父类一模一样的函数时,当子类对象调用
该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。
这种情况是函数的另一个特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能到子类中,
但是子类虽具备该功能,但是功能内容却和父类不一致,
这时,没有必要定义新功能,而是用覆盖特性,保留父类的功能定义,
并重写功能内容
注意:
1,子类覆盖父类,必须保证子类权限大与等于父类权限,才可以覆盖,否则编译失败。
2,静态不能覆盖非静态,静态只能覆盖静态,
记住:
重载:只看同名的参数列表。
覆盖:子父类方法要一模一样(子类的权限必须是大于或等于父类的权限)。
子类构造函数特点:
子类所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有一句隐式super();
当父类中没有有空参数的构造函数时,
子类必须手动通过super语句形式来指定要访问父类中的构造函数。
当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数
代码示例:
class Fu { public Fu(String name) { System.out.println("fu..." + name); }}class Zi extends Fu { public Zi() { // super();//此语句不能执行,因为父类中没有空构造函数 super("haha"); // 因为父类中没有默认空构造函数,子类必须手动通过super语句制定要访问的父类构造函数 System.out.println("zi..run"); } public Zi(String name) { this(); // super(name);//错误,因为super()必须放在构造函数的第一行,一个构造函数中,super和this只能存在一个 System.out.println("zi..." + name); }}public class Demo { public static void main(String[] args) { new Zi();//fu...haha zi..run new Zi("haha");//fu...haha zi..run zi...haha } }
抽象类:
当多个类中出现相同功能,但是功能主体不同,
这时可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体
抽象:看不懂的东西就叫抽象
抽象类的特点:
1.抽象方法一定在抽象类中
2.抽象方法和抽象类必须由abstract关键字修饰
3.抽象类不可以用new创建对象,因为抽象方法没有方法体,调用该抽象方法没意义。
4.抽象类中的方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么子类必须也为抽象类
抽象类和一般的类没有太大的不同。
该如何描述事物就如何描述事物,只不过,该事物出现了一些看不懂的东西。
这些不确定的部分也是该事物的功能,需要明确出现。但是无法定义主体,
通过抽象方法来表示。
抽象类和普通类比较:抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法。抽象类不可以创建对象。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让类建立对象
什么时候用抽象方法:子类必须从父类拿过来且需要自己改写的,就定义为抽象方法(注意:是必须拿过来)。子类
需要原封不动的拿过来用的,就定义成非抽象方法。
代码示例:
/* * 需求: *对员工建模,员工包含3个属性:姓名、工号、工资。经理也是员工,除了含有员工的属性外, *另外还有一个奖金属性。请用继承的思想设计出员工类和经理类,要求类中提供必要的方法 *进行属性访问 */abstract class YuanGongDemo { private String name; private int gonghao; private int gongzi; public YuanGongDemo(String name, int gonghao, int gongzi) { this.name = name; this.gonghao = gonghao; this.gongzi = gongzi; } /* 因为员工必须要工作,但是工作的形式又不同,所以定义为抽象的 例如:员工的工作是敲代码,经理也是员工,但是经理的工作是负责看管其他的员工敲代, 经理的工作和员工都必须工作,但是他们的工作不同,所以定义为抽象的。 简单的说:当多个类中出现相同功能,但是功能主体不同,那就可以把该功能定义为抽象的 */ public abstract void gongzuo();//由于每个员工都需要工作, //但是工作的方式不同,所以定义为抽象的}class YuanGong extends YuanGongDemo{ public YuanGong(String name, int gonghao, int gongzi) { super(name, gonghao, gongzi); } public void gongzuo(){ System.out.println("敲代码"); }}class JingLi extends YuanGongDemo { private int jiangjin; public JingLi(String name, int gonghao, int gongzi, int jiangjin) { super(name, gonghao, gongzi); this.jiangjin = jiangjin; } public void gongzuo(){ System.out.println("看管员工敲代码"); }}
接口:
interface 用于定义接口
接口定义时,格式特点:
1.接口中常见定义:常量、抽象方法。
2.接口中的成员都有固定的修饰符
常量:public static final
方法:public abstract
记住:接口中的成员都是public的。
接口不可以创建对象,因为有抽象方法。
需要被子类实现,子类对接口中的抽象方法全部覆盖后,子类才可以实例化(创建对象)。
否则子类是一个抽象类
接口可以多继承
接口:用于定义扩展功能
类: 用于定义基本功能
如:
1.在学生体系中,学生有学习和睡觉,学习和睡觉是基本功能。
而有些学生抽烟,抽烟是扩展功能,因为抽烟并不是每个学生都必须具备的功能。
2.在运动员体系中,运动员有打球和睡觉,打球和睡觉是基本功能。
而有些运动员学习java,学习java 是扩展功能,因为并不是每个运动员都必须要学习java
代码示例:
/* 接口的多继承 */interface A{ public abstract methodA();}interface B{ public abstract methodB();}interface C extends A,B{//C继承A的同时继承B public abstract methodC();}
/* 接口的使用方式 *///定义一个接口interface Somking{ //抽烟的方法。因为抽象不是学生该具备的功能,它属于扩展功能,所以定义在接口中 public abstract void somke();}//定义一个学生类,每一学生都需要睡觉和学习,将其方法封装在学生类中abstract class Student{ abstract void study();//学习的方法,因为每个学生都要学习,但是学习的的方式不同,所以定义为抽象 void sleep(){ System.out.println("睡觉");//睡觉方法。因为每个学生睡觉的方式都一样,所以不是抽象的 }}class ZhangSan extends Student{ void study() { System.out.println("zhangsan:学习java"); }}class LiSi extends Student implements Somking{//因为lisi抽烟,所以事先Somking接口 void study() { System.out.println("lisi:学习c++"); } public void somke() { System.out.println("lisi:抽烟"); }}public class Demo { public static void main(String[] args) { ZhangSan zs = new ZhangSan(); zs.study();//zhangsan:学习java zs.sleep();//睡觉 LiSi ls = new LiSi(); ls.study();//lisi:学习c++ ls.sleep();//睡觉 ls.somke();//lisi:抽烟 }}
一些需要注意的细节:
直接输出一个对象时,调用的是对象.toString()方法输出一个字符串。此方法是Object中的方法, 默认输出的结果是:getClass().getName() + '@' + Integer.toHexString(hashCode()) 既:对象名@十六进制的哈希码。 如:Demo@6af62373 Demo是对象名,6af62373是十六进制的哈希码 想要输出指定的值,对象可以覆盖Object类中的toString,返回一个自己希望的字符串, 那么再直接输出一个对象时,输出结果就是自己定义的那个字符串
面向对象的简单总结:
1,以后开发:其实就是找对象使用。没有对象,就创建一个对象。2,类:就是对现实生活中事物的描述 对象:就是这类事物,实实在在存在的物体3,引用数据类型包含三种:数组、类、接口 只要new一次对象,堆内存中就产生一个新对象 堆内存中的变量:一被定义都有一个默认的值4,成员变量和局部变量: 作用范围: 成员变量:作用于整个类中。定义在类中的变量。 局部变量:作用于函数中,或者语句中。 在内存中的位置: 成员变量:在堆内存中,因为对象的存在,才在内存中存在。定义在类中的变 量都是成员变量。5,匿名对象使用方式一: 当对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。 如果一个对象进行多个成员调用,必须给这个对象起个名字 匿名对象使用方式二: 可以将匿名对象作为实际参数进行传递6,构造代码块: 作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数执行。7,this:this代表它所在函数所属对象的引用。简单说:哪个对象在调用this所在的 函数,this就代表哪个对象。8,当成员被静态修饰后,就多了个调用方式,除了可以被对象调用外,还可以直接被 类名调用。类名.静态成员。静态的数据存在于方法区中 static特点: 1.随着类的加载而加载。 也就是说:静态会随着类的消失而消失。说明它的生命周期最长。 2.优先于对象存在 明确一点:静态是先存在,对象是后存在的。 3.被所有对象共享 4.可以直接被类名调用9,final: 1.可以修饰类,函数,变量 2.被final修饰的类不可以被继承 3.被final修饰的方法不可以被覆盖,但是可以重载 4.被final修饰的变量是一个常量,只能被赋值一次,既可以修饰成员 变量,也可以修饰局部变量 (注意:static只能修饰成员变量) 5.书写规范:所有字母都大写,如果由多个单词组成,那么就通过 _ 连接10,在多态中成员函数的特点: 在编译时期:参阅引用变量所属的类中是否有调用的方法(左边)。如果有,编译 通过,如果没有编译失败。 在运行时期:参阅对象所属的类中是否有调用的方法(右边),如果有,则执行右 边,没有,则执行左边 简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。 在多态中,成员变量的特点: 无论在运行还是编译时,都参考左边(引用型变量所属的类)。 在多态中,静态成员函数的特点: 无论运行还是编译时,都参考左边 在多态中,静态变量的特点: 无论运行还是编译时,都参考左边
- 黑马程序员—面向对象
- 黑马程序员—面向对象
- 黑马程序员—面向对象
- 黑马程序员——面向对象1:理解面向对象
- 黑马程序员———-面向对象
- 黑马程序员———面向对象
- 黑马程序员———面向对象
- 黑马程序员———面向对象
- 黑马程序员———面向对象
- 黑马程序员——面向对象
- 黑马程序员——面向对象总结
- 黑马程序员——面向对象
- 黑马程序员——C#面向对象
- 黑马程序员——面向对象
- 黑马程序员——面向对象
- 黑马程序员——java面向对象
- 黑马程序员——C#面向对象
- 黑马程序员——面向对象1
- 编程路漫漫~
- [Object c]_[初级]_[NSString常用方法的总结]
- 关闭Tomcat后再打开后,session还在,不进登陆页面
- Ubuntu 下配置 SSH服务全过程及问题解决
- 桶排
- 黑马程序员——面向对象
- Linux下利用backtrace追踪函数调用堆栈
- zoj2770_Burn the Linked Camp
- MySQL 中文显示乱码
- 下面是什么用法
- 安卓蓝牙开发中google例子BluetoothChat的问题
- android EditText+ListView的组合(类似于AutoCompleteTextView)
- linux下添加PATH的方法
- c# webkit 模拟点击