Java_对象

 

面向对象是一种现在最为流行的程序设计方法。但是在面向对象设计之前,广泛使用的是面向过程编程。

所谓面向过程开发,即只是针对自己来解决问题。以程序基本功能实现为主,实现之后就算完成了,也不考虑修复、复用等。

面向对象,更多的是一种模块化设计;每一个模块都可以单独存在,并可复用;面向对象的开发更像是一种标准的开发模式。

类与对象:

类与对象是面向对象开发中最基础的组成单元。

类:是一个抽象的集合,表示的是一个共性的产物;类之中定义的是属性和行为(方法)。

对象:对象是一种个性的表示,表示一个单独的个体;每个对象可以拥有自己独立的属性,可以根据属性来区分不同的对象。

用一句话来总结类与对象的关系:类是对象的模板,对象是类的实例。类只有通过对象才可以实现,在开发中应该先产生类,之后再定义对象。类不能直接使用,对象是可以直接使用的。

 

 语法与使用:

eg:

//定义一个Person类
class Person { //类名称 首字母大写
	String name; //属性
	int age; //属性
	
	public void tell(){ //方法
		System.out.println("姓名:"+name+",年龄:"+age);
	}
}

 

类定义之后是无法直接使用的。必须先定义一个对象。由于类属于引用数据类型,所以对象的产生格式(有两种)如下

(1) 格式一 :声明并实例化对象;

类名 对象名 = new 类名();

(2) 格式二:先声明对象,然后实例化对象;

类名 对象名 =null;

对象名 = new 类名();

引用数据类型与基本数据类型最大的区别在于:引用数据类型需要内存的分配和使用。所以关键字new的主要功能就是分配内存空间。也就是说,只要是引用数据类型,就必须使用new来分配内存。

当一个实例化对象产生后,就可以按照如下的方式进行类的操作:

对象.属性:表示调用类中的属性;

对象.方法:表示调用类中的方法。

eg:

package javaEE;
/** 
 * @author R
 * @version 2018年8月15日 
 * 类说明 
 */

//定义一个Person类
class Person { //类名称 首字母大写
    String name; //属性
    int age; //属性
    
    public void tell(){ //方法
        System.out.println("姓名:"+name+",年龄:"+age);
    }
}

public class Test1 { 
    public static void main(String[] args) {
        Person per = new Person();
        per.name="小宇";
        per.age=25;
        per.tell();
    }
}

 

 

面向对象的封装性:

封装又叫隐藏实现,就是只公开代码单元的对外接口,隐藏其具体实现。

即将类中的属性定义为私有,用private修饰。外部的对象无法直接调用类中的属性,即对外不可见。

外部的对象若想访问private修饰的属性,要定义属性对应的setter方法和getter方法。

setter方法是设置属性的内容,有参数;

public void setTitter(string tittle);

getter方法是获取属性的内容,无参数;

public String getTitle();

eg:

package javaEE;
/** 
 * @author R
 * @version 2018年8月16日 
 * 类说明 
 */
class Book{
    private String title;
    private double price;
    
    public void setTitle(String title){
        this.title = title;
    }
    public String getTitle(){
        return title;
    }
    
    public void setPrice(double price){
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public void getInfo(){
        System.out.println("图书的名字是:"+title+",图书的价格是:"+price);
    }
}

public class Test2 {
    public static void main(String[] args) {
        Book book = new Book();
        book.setTitle("Java程序设计");
        book.setPrice(139.0);
        book.getInfo();
    }
}

 

类中的构造方法:

先看对象的产生格式:

类名(1) 对象名(2) = new(3) 类名()(4);

(1)类名:规定了对象的类型,即对象可以使用哪些属性或方法是由类决定的。

(2)对象名:如果需要使用对象,则需要一个名词,这是一个唯一的标识。

(3)new:分配新的堆内存

(4)类名():调用了名称和类名称相同的方法,即 构造方法。

构造方法的定义:

方法名称和类名称相同,没有返回值。

说明:

实际上,构造方法一直在被我们调用,但是我们却没有定义它,为什么能够使用呢?这是因为在整个Java开发中,为了保护程序的正常运行,虚拟机会在程序编译后自动为类增加一个没有参数,名称与类名称相同,没有返回值的构造方法。

//无参构造方法
public Book(){

}

 

再看:

package javaEE;
/** 
 * @author R
 * @version 2018年8月16日 
 * 类说明 
 */
class BookS{
	String title;
	double price;
	public BookS(){
		System.out.println("无参构造方法");
	} 
}

public class Test3 {
	public static void main(String[] args) {
		BookS book = null;  //声明对象
	}
}

 运行,什么都没有打印。

package javaEE;
/** 
 * @author R
 * @version 2018年8月16日 
 * 类说明 
 */
class BookS{
	String title;
	double price;
	public BookS(){
		System.out.println("无参构造方法");
	} 
}

public class Test3 {
	public static void main(String[] args) {
		BookS book = null;  //声明对象
		book = new BookS();  //实例化对象
	}
}

 运行:

无参构造方法

以上说明构造方法是在对象使用关键字new 实例化的时候被调用。

构造方法与普通的方法最大的区别在于:

构造方法在实例化对象(new)的时候只调用一次,而普通方法在实例化对象之后可以多次调用。

在实际开发中,构造方法的作用是在类对象实例化的时候设置属性的初始值。

eg:

package javaEE;
/** 
 * @author R
 * @version 2018年8月17日 
 * 类说明 
 */
class BookA{
	private String titleName;
	private double price;
	
	public BookA(String titleName,double price){
		this.titleName = titleName;
		this.price = price;
	}
	
	public void setTitleName(String titleName){
		this.titleName = titleName;
	}
	public String getTitleName(){
		return titleName;
	}
	
	public void setPrice(double price){
		this.price = price;
	}
	public double getPrice(){
		return price;
	}
	
	public void getInfo(){
		System.out.println("图书的名称是:"+titleName+",图书的价格是:"+price);
	}
}

public class Test4 {
	public static void main(String[] args) {
		BookA book = new BookA("大数据学习",100);
		book.getInfo();
	}
}

 运行:

图书的名称是:大数据学习,图书的价格是:100.0

 说明:如果一个类中已经明确定义了一个构造方法,那么无参的构造方法将不会生成。且,一个类中最少要有一个构造方法。

另外,构造方法也属于方法,那么构造方法也是可以重载的。

eg:

package javaEE;
/** 
 * @author R
 * @version 2018年8月17日 
 * 类说明 
 */
class BookB{
	private String name;
	private double price;
	
	public void setName(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
	
	public void setPrice(double price){
		this.price = price;
	}
	public double getPrice(){
		return price;
	}
	
	public BookB(){
		System.out.println("无参的构造方法");
	}
	public BookB(String name){
		this.name = name;
		System.out.println("有一个参数的构造方法");
	}
	public BookB(String name,double price){
		this.name = name;
		this.price = price;
		System.out.println("有两个参数的构造方法");
	}
	public void getInfo(){
		System.out.println("图书的名字是:"+name+",图书的价格是:"+price);
	}
}

public class Test5 {
	public static void main(String[] args) {
		BookB book1 = new BookB();
		book1.getInfo();
		
		BookB book2 = new BookB("大数据开发");
		book2.getInfo();
		
		BookB book3 = new BookB("大数据开发",100);
		book3.getInfo();
	}
}

 运行:

无参的构造方法
图书的名字是:null,图书的价格是:0.0
有一个参数的构造方法
图书的名字是:大数据开发,图书的价格是:0.0
有两个参数的构造方法
图书的名字是:大数据开发,图书的价格是:100.0

如果我们在类的属性中设置默认值,结果会如何?

package javaEE;
/** 
 * @author 047493 
 * @version 2018年8月17日 
 * 类说明 
 */
class BookB{
    private String name = "Android";
    private double price = 200;
    
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    
    public void setPrice(double price){
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    
    public BookB(){
        System.out.println("无参的构造方法");
    }
    public BookB(String name){
        this.name = name;
        System.out.println("有一个参数的构造方法");
    }
    public BookB(String name,double price){
        this.name = name;
        this.price = price;
        System.out.println("有两个参数的构造方法");
    }
    public void getInfo(){
        System.out.println("图书的名字是:"+name+",图书的价格是:"+price);
    }
}

public class Test5 {
    public static void main(String[] args) {
        BookB book1 = new BookB();
        book1.getInfo();
        
        BookB book2 = new BookB("大数据开发");
        book2.getInfo();
        
        BookB book3 = new BookB("大数据开发",100);
        book3.getInfo();
    }
}

运行:

无参的构造方法
图书的名字是:Android,图书的价格是:200.0
有一个参数的构造方法
图书的名字是:大数据开发,图书的价格是:200.0
有两个参数的构造方法
图书的名字是:大数据开发,图书的价格是:100.0

 

类中的匿名对象

没有名字的对象叫做匿名对象,即,没有栈内存指向堆内存空间,就是一个匿名对象。

匿名对象由于没有对应的栈内存指向,所以只能使用一次,一次之后就将成为垃圾,并且等待被GC回收释放。

 

深入理解封装、继承、多态:

  封装:首先是抽象,把事物抽象成一个类。其次才是封装,将事物拥有的属性和方法隐藏起来,只保留特定的方法与外界联系。

  封装的好处:(1)良好的封装能够减少耦合;

        (2)类内部的结构可以自由修改;

        (3)可以对成员进行更精准的控制;

        (4)隐藏信息,实现细节。

举个栗子:

package java_02;
/** 
 * @author 047493 
 * @version 2018年9月3日 
 * 类说明 
 */
public class Husband {
    
    /*
     * 对属性的封装,一个人的姓名、年龄、妻子都是这个人的私有属性
     * */
    private String name;
    private int age;
    private Wife wife;
    
    /*
     * setter(),getter()是该对象对外开发的接口
     * */
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
    
    public void setWife(Wife wife){
        this.wife = wife;
    }
}
package java_02;
/** 
 * @author 047493 
 * @version 2018年9月3日 
 * 类说明 
 */
public class Wife {
    
    private String name;
    private int age;
    private Husband husband;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public Husband getHusband() {
        return husband;
    }
    public void setHusband(Husband husband) {
        this.husband = husband;
    }
    
}


上面两个类,可以发现 Husband隐藏了wife的getter(),Wife隐藏了age的getter()。

所以封装把一个对象的属性私有化,同时提供一些可以被外界访问的方法,但如果不想被外界访问,我们大可不必提供方法给外界访问。

但一个类,如果没有提供外界访问的方法,那么这个类也是没有存在的意义的。

如果把房子比作一个对象,里面的各种漂亮装饰,如沙发、电视、空调等都是这个房子的私有属性。

但如果这个房子没有遮挡物,是不是别人就会一览无余呢?没有一点隐私。

就是存在遮挡的墙,我们既能有自己的隐私又能随意的更改里面的摆设而不会影响到其他人。

但是如果没有门窗,一个包裹的严严实实的黑盒子也是没什么意义的。

所以通过门窗可以看到房子里面的接口,即门窗就是房子留给外界访问的接口。

 

站在程序的角度上,如果不使用封装,那么Husband类我们应该这样写:

package java_02;
/** 
 * @author 047493 
 * @version 2018年9月3日 
 * 类说明 
 */
public class Husband {
    
    public String name;
    public int age;
    public Wife wife;
    
    
    public static void main(String[] args) {
        Husband husband = new Husband();
        husband.name="Ethan";
        husband.age=26;
        
    }
}


但是如果某天我们将age属性由int类型修改成String类型呢?如果你只有一个地方使用了这个类还好,如果几十上百个类都使用了这个类,岂不是要改到地老天荒?

但是如果我们使用了封装,那么只需修改Husband的setAge()即可。

package java_02;
/** 
 * @author 047493 
 * @version 2018年9月3日 
 * 类说明 
 */
public class Husband {

    private String name;
    private String age;
    private Wife wife;
        
    public String getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = String.valueOf(age);  //转换即可
    }
    
    ...
}

其他地方仍然可以继续引用husband.setAge(26);保持不变。
我们可以看到,封装确实可以使我们更加容易的修改类的内部实现,而无需修改使用了该类的客户代码。

下面再看封装的这个优点,可以对成员变量进行更精准的控制。

假如,某天,你脑抽了 写下如下代码:

Husband husband = new Husband();
    husband.age=300;

那也许就闹笑话了。。。
但是使用封装我们就可以避免这个问题,比如对setter接口做一些控制,如:

public int getAge(){
    return age;
}
public void setAge(int age){
    if(age > 120){
        System.out.println("可能是个老妖怪了。。。");
    }else{
        this.age = age;  //转换
    }
}

上面都是对setter()的控制,其实通过使用封装,我们也能够对对象的出口做很好的控制。
例如性别在数据库中我们一般都是以0 、1来存储的,但是展现在页面又不能直接以0 、1展示,这里我们只需要在getter()里面做一些转换即可。

public String getSexName() {
        if("0".equals(sex)){
            sexName = "女";
        }
        else if("1".equals(sex)){
            sexName = "男";
        }
        else{
            sexName = "人妖???";
        }
        return sexName;
}

 

 继承:简单来说就是子类复用父类的属性或方法。

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性的继承父类。

继承所描述的是“is-A”的关系,如果有两个对象A和B,若可以描述为 A是B,则可以表示A继承B,其中B是被继承者称之为父类或超类,A是继承者称之为子类或派生类。

实际上继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独特的特性。例如猫有抓老鼠,爬树等其他动物没有的特性。同时在继承关系中,继承者完全可以替代被继承者,反之则不可以。例如我们可以说猫是动物,但不能说动物是猫。对于这一点,我们称之为 “向上转型”。

诚然,继承定义了类如何互相关联,共享特性。

在使用继承时需注意:

(1)子类拥有父类非private的属性和方法;

(2)子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;

(3)子类可以用自己的方法实现父类的方法。

举个栗子:

public class Person {
    protected String name;
    protected int age;
    protected String sex;
    
    Person(){
        System.out.println("Person Constructor...");
    }
}

public class Husband extends Person{
    
    private Wife wife;
    
    Husband(){
        System.out.println("Husband Constructor...");
    }
    
    public static void main(String[] args) {
        Husband husband = new Husband();
    }
    
}

运行结果如下:

Person Constructor...
Husband Constructor...

通过这个栗子我们可以看出,构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级的完成构建。而且我们并没有显式的引用父类的构造器,这就是Java的聪明之处:编译器会默认给子类调用父类的构造器。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就必须显示的使用super()来调用父类的构造器,否则编译器会报错:无法找到符合父类形式的构造器。

举个栗子:

public class Person {
    protected String name;
    protected int age;
    protected String sex;
    
    Person(String name){
        System.out.println("Person Constructor..."+name);
    }
}

public class Husband extends Person{
    
    private Wife wife;
    
    Husband(){
        super("Ethan");
        System.out.println("Husband Constructor...");
    }
    
    public static void main(String[] args) {
        Husband husband = new Husband();
    }
    
}

运行结果如下:

Person Constructor...Ethan
Husband Constructor...

综上所述:对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显式的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。

关于protected:

private修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界。有的时候,我们需要这样的需求:我们需要将某些事物尽可能的对这个世界隐藏,但是仍然允许子类的成员来访问它们。这个时候就需要使用到protected。

对于protected,它指明就类用户而言,它是private;但是对于任何继承的子类或任何位于同一包的类而言,它是可以访问的。

举个栗子:

public class Person {
    private String name;
    private int age;
    private String sex;
    
    protected String getName() {
        return name;
    }
    protected void setName(String name) {
        this.name = name;
    }
    //...
    
    public String toString(){
        return "this name is "+name;
    }
}

public class Husband extends Person{
    
    private Wife wife;
    
    public String toString(){
        setName("Ethan");  //调用父类的setName()
        return super.toString();  //调用父类的toString()
    }
    
    public static void main(String[] args) {
        Husband husband = new Husband();
        System.out.println(husband.toString());
    }
}

运行结果如下:

this name is Ethan

可以看到子类Husband明显的调用了父类Person的setName()。
需要注意的是:尽管可以使用protected修饰符来限制父类属性和方法的访问权限,但是最好的方式还是讲属性保持为private,通过protected方法来控制类的继承者访问权限。

关于向上转型:

在上面的继承中我们谈到继承是is-A的相互关系,例如猫继承于动物,所以我们可以说猫是动物,或者说猫是动物的一种,这样将猫看做动物就是向上转型。

举个栗子:

public class Person {
    
    public void display(){
        System.out.println("Person display...");
    }
    
    static void display(Person person){
        person.display();
    }
}

public class Husband extends Person{
    
    public static void main(String[] args) {
        Husband husband = new Husband();
        Person.display(husband);
    }
}

运行结果如下:

Person display...

在这我们通过Person.display(husband);可以看出husband是Person类型。

将子类转换成父类,在继承关系上是向上移动的,所以称之为向上转型。由于向上转型是从一个专用类型向通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。

这就是为什么编译器在“未曾明确表示转型” 或 “未曾指定特殊标记”的情况下,仍然允许向上转型的原因。

 

继承的缺点:

(1)父类变,子类就必须变;

(2)继承破坏了封装,对于父类而言,它的实现细节对于子类来说都是透明的;

(3)继承是一种强耦合的关系。

慎用继承!!!

 

多态:简单来说,就是指一个引用(类型)在不同情况下的多种状态。也可以理解为,多态是通过指向父类的指针,来调用在不同子类中实现的方法。

https://www.cnblogs.com/1693977889zz/p/8296595.html

 

参考地址:https://blog.csdn.net/wei_zhi/article/details/52745268

        https://blog.csdn.net/wei_zhi/article/details/52750933

     https://www.cnblogs.com/chenssy/p/3351835.html(封装)

     https://www.cnblogs.com/chenssy/p/3354884.html(继承)

     https://www.cnblogs.com/chenssy/p/3372798.html(多态)

     

 

posted @ 2018-08-17 16:48  一枚路过的小码农  阅读(181)  评论(0)    收藏  举报