JAVA面向对象基础

JAVA面向对象基础

一、类与实例

对象:万物皆对象,所有的东西都是对象,对象是一个自包含的实体,用一组可识别的特性和行为来标识。

:是具有相同属性和功能的对象的抽象集合

实例:就是一个真实的对象

实例化:创建对象的过程,使用new关键字来创建

public void Test()
{
	Cat cat = new Cat();//将Cat类实例化
}

'Cat cat = new Cat();'做了两件事

Cat cat;//声明一个Cat的对象,对象名为cat
cat = new Cat();//将此cat对象实例化

二、构造方法

构造方法又叫构造函数,就是对类进行初始化。与类同名,无返回值,不需要void,在new的时候调用。

Cat cat = new Cat()

其中,Cat()就是构造方法。

所有类都有构造方法,不定义构造方法系统会默认生产空的构造方法。定义构造方法,则默认的构造就方法会消失。

public class Cat
{
    //声明Cat类的私有字符串变量name
    private String name = "";
    //定义Cat类的构造方法,参数是输入一个字符串。
    public Cat(String name)
    {
        this.name = name;//将参数赋值给私有变量name
    }
}

三、方法重载

方法重载提供创建同名的多个方法的能力,但是这些方法需使用不同的参数类型,不只构造方法可以重载,普通方法也可以重载。

方法名相同,参数类型或个数必须要有所不同。

方法重载可在不改变原方法的基础上,新增功能。

public class Cat
{
    private String name = "";
    public Cat(String name)
    {
        this.name = name;
    }
    //将构造方法重载
    public Cat()
    {
        this.name = "无名";
    }
}

四、属性与修饰符

属性

属性:是一个方法或一对方法,在调用它的代码看来,它是一个字段,即属性适合于以字段的方式使用方法调用的场合。

字段:是存储类要满足其设计所需要的数据,字段都是与类相关的变量。

//声明一个内部字段,private,默认为3
private int shoutNum = 3;
//ShoutNum属性,注意是public
public int ShoutNum
{
    //get表示外界调用时可以得到shoutNum的值
    public int get(){
        return shoutNum;
    }
    //set表示外界可以给内部的shoutNum赋值
    public void set(int value){
        shoutNum = value;
    }
}

属性有两个方法,get和set。

get:访问器返回与声明的属性相同的数据类型,调用时可以得到内部的字段的值或引用。

set:调用属性时可以给内部的字段或引用赋值。

修饰符

public:表示它所修饰的类成员可以允许其它任何类来访问,俗称公有的。

private:表示只允许同一个类中的成员访问,其它类包括它的子类无法访问,俗称私有的。

通常字段都是private,即私有变量(一般是首字母小写或前加‘_’),而属性都是public,即公有变量(首字母大写)。

final

  1. 修饰变量:修饰变量时必须赋初值且不呢改变,修饰引用变量不能再指向其他对象。
  2. 修饰方法:方法前面加上final关键字,代表这个方法不可被子类重写。
  3. 修饰类:表示这个类不能被继承,类中的成员可以根据需要设为final,final类中的所有成员方法都会被隐饰地指定为final方法。

注:类的private方法会被隐饰地指定为final

protected:继承的子类可以对基类(父类)有完全访问权。对子类公开,不对其它类公开。

五、封装

每个对象都包含它能进行操作所需的所有信息,因此对象不必依赖其它对象来完成自己的操作,能够避免对象属性赋值的随意性。

封装的好处:

  1. 良好的封装能减少耦合(相互作用相互影响的关系)。
  2. 类内部的实现可以自由地修改。
  3. 具有清晰的对外接口。(对外的属性和方法)set和get方法。

this关键字是对当前内的简化。

提供专门对外的set和get方法对属性进行设置,而不能用类.属性的方法修改值。

public class Student{
    private int age;//age属性私有化
    
    public Student(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
}

六、继承

java只支持单继承,不允许多继承。

子类继承父类:

  1. 子类拥有父类所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类无法访问,只是拥有
  2. 子类拥有自己的属性和方法,即子类可以在父类的基础上做扩展。
  3. 子类可以用自己的方式重写父类的方法。
package Test;

class Students {
    int age;//id属性私有化
    String name;//name属性私有化
    Students(int age,String name){
    	this.age = age;
    	this.name = name;
    }
    public void printInfo() {
		System.out.println("父类方法 年龄:"+age+" 姓名:"+name);
	}
}

class Student extends Students{
	Student(int age, String name) {
		super(age, name);
	}
	//重写父类同名方法
    @Override
	public void printInfo() {
		System.out.println("子类方法 年龄:"+age+" 姓名:"+name);
	}
	//使用super调用父类的方法
	public void FatherprintInfo() {
		super.printInfo();
	}
}
public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Student s2 = new Student(19,"张三");
		s2.printInfo();
		s2.FatherprintInfo();
	}
}

//子类方法 年龄:19 姓名:张三
//父类方法 年龄:19 姓名:张三

super关键字

  1. super用于在子类调用父类的重名方法(不重名也能引用)。
  2. super在子类中引用重名的变量(不重名也能引用)。
  3. super()可以用于直接调用父类的构造方法。

继承的优点

继承使得所有子类公共的的部分都放在了父类,使得代码得到了共享,就可以避免重复,继承使得修改和扩展继承而来的实现都较为容易。

继承的缺点

继承也是有缺点的,那就是父类做出修改,子类就不得不进行改变。另外,继承也会破坏包装,父类实现的细节暴露给子类。

总结:只有合理的应用继承才能发挥好的作用。例如猫继承动物,而动物不能继承猫。

七、多态的浅理解

多态表示不同的对象执行相同的操作,但是要通过自己的实现代码来执行。

注意点:

  1. 子类以父类的身份出现。
  2. 子类在工作时以自己的方式来实现。
  3. 子类以父类身份出现时,子类特有的属性和方法不可以使用,只能用父类存在的方法。

多态有两种情形

编译时多态:

  1. 重载(overload)多个同名的不同方法。

运行时多态:

  1. 覆盖(override),子类对父类方法进行覆盖(重写)。
  2. 动态绑定--也称为虚方法调用,正真的方法在运行时才确定。
  3. 在调用方法时,程序会正确地调用子类对象的方法。

多态中成员的特点

1.多态成员变量:编译运行看左边。

People p = new Student();
System.out.println(p.name)//p是People中的值,只能取到父类中的值。

2.多态成员方法:编译看左边,运行看右边。

People p1 = new Student();
System.out.println(p1.printInfo())//p1的表面上类型是People,但实际上是Student,所以调用的是重写后的方法。

instancof关键字

用于判断某个对象是否属性某种数据类型。

返回类型为布尔类型。

People p1 = new Student();
System.out.println(p1 instancof Student);
//true

多态的转型

向上转型

将子类对象赋值给父类变量,称之为向上转型。

//People[] people = {new Student(19,"张三"), new Teacher(30,"老师A")};
People people[];//声明一个人类数组
people = new People[1];//实例化最多两个人类对象
//向上转型
people[0] = new Student(19,"张三");
people[1] = new Teacher(30,"老师A");
//foreach遍历对象
for(People item : people)
{
    item.printInfo();
}
//年龄:19 姓名:张三
//年龄:30 姓名:老师A

向下转型

将父类变量转换为子类变量,称之为向下转型。向下转型格式如下:

子类类型 变量名 = (子类类型) 父类类型的变量
for(int i=0;i<people.length;i++){
    if(people[i] instanceof Student){
    	//向下转型
        Student student = (Student) people[i];
	}else if(people[i] instanceof Teacher){
        Teacher teacher = (Teacher) people[i];
    }else{
        System.out.println("程序错误。");
    }
}

八、抽象类

仔细观察,你会发现,People类其实根本不能实例化,一个学生长什么样子,可以想象。new People;即实例化一个人,一个人长什么样?

People是一个抽象的名词,没有其具体对象与之对应。

Java运行把类和方法声明为abstract,即抽象类和抽象方法。

abstract class People{//加abstract关键字,表面抽象类。
	......
     //在方法返回值前加abstract表明此方法是抽象方法,抽象方法没有方法体,直接在括号后面加上";"
	protected abstract void printInfo();
}

抽象类注意点:

  1. 抽象类不能实例化。
  2. 抽象方法必须被子类重写。
  3. 如果类中有抽象方法,那么类就必须定义为抽象类,不论是否还包含其它的一般方法。

让抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。

抽象类通常代表一个抽象的概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所有在一个继承关系形成的等级结构里,树叶节点应当是具体类。而树叶节点均应当是抽象类。

九、接口(interface)

接口就是把隐式公共方法和属性组合起来,以封装特定功能的一个集合,一旦类实现了接口,类就可以职称接口所指定的所有属性和成员,声明接口的语法和抽象类完全相同,但不允许提供接口中任何成员的执行方式。所以接口不能实例化,不能有构造方法和字段;不能有修饰符(如public,private等);不能声明虚拟的或静态的等。还有实现接口的类就必须要属性接口中的所有方法和属性。

一个类可以支持多个接口,多个类也可以支持相同的接口。接口的命名,前面要加一个大写的字母'I',这是规范。

接口用interface声明,而不是class,接口名称前要加‘I’,接口中的方法或属性前面不能有修饰符、方法没有方法体。

//声明一个IPeople接口,此接口有一个printInfo方法
interface IPeople{
    void printInfo();
}

使用接口实现学生类

class Student implements IPeople{
    public void printInfo(){
        System.out.println("运行正确。");
    }
}

区分抽象类和接口

抽象类可以给出一些成员的实现,接口却不包含成员的实现,抽象类的抽象成员可被子类部分实现,接口的成员需要实现类完全实现,一个类只能继承一个抽象类,但可实现多个接口等。

区分:

  1. 类是对对象的抽象;抽象类是对类的抽象;接口是对行为的抽象。
  2. 如果行为跨越不同类的对象,可使用接口;对于一些相似的类对象,用继承抽象类。(实现接口和继承抽象类并不冲突)。
  3. 从设计者角度来讲,抽象类是从子类对象中发现了公共的东西,泛化出父类,然后子类继承父类。而接口是根本不知子类的存在,方法如何实现还不确认,提前定义。

十、泛型

泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所储存或使用的一个或多个类型的占位符。泛型集合类可以将类型参数用作它所存储的对像的类型的占位符;类型参数作为其字段和其方法的参数类型出现。

IList arrayPeople;//声明一个集合变量,可以用接口IList,也可以直接声明“ArrayList arrayPeople;”

用法就是在IList和List后面加'',这个'T'就是你需要指定的集合的数据或对象类型。

//声明一泛型集合变量,用接口IList,注意:IList表示此集合变量只能接受People类型,其它类型不接受,也可以直接声明“List ”
IList<People> arrayPeople;
//实例化List对象,需要指定List<T>的'T'是People
arrayPeople = new List<People>();

泛型的使用

泛型有三种使用的方式,分别为:泛型类、泛型接口、泛型方法。

public class People<T>{
	//T stands for "Type"
    private T t;
    public void set(T t){this.t = t;}
    public T get(return t);
}

十一、注解

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明,配置的功能。注解不会也不呢影响代码的实际逻辑,仅仅起到辅助性的作用。

注解 Annotation 实现原理与自定义注解例子(opens new window)

十二、反射

什么是反射

可以获取任意一个类的所有属性和方法,还可以调用这些属性和方法。

Java反射主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  • 在运行时调用任意一个对象的方法;

重点:是运行时而不是编译时。

反射机制的优缺点

  • 优点:可以让代码更加灵活,为各种框架提供开箱即用的功能提供了便利。
  • 缺点:让我们在运行时有了分析操作类的能力,增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也会稍差点。不过,对框架来说影响不大。Java Reflection: Why is it so slow?

反射的应用场景

我们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。

但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。

十三、异常

在Java中,所有的异常都有一个共同的祖先java.lang包中的Throwable类,Throwable类有两个重要的子类Error(错误)和Exception(异常)。其中Error用来表示JVM(Java虚拟机)无法处理的错误,Exception又分为两种:

  • 受检查异常:需要用try-catch语句捕获并进行处理,并且可以从异常中恢复。
  • 不受检查异常:java代码在编译过程中,即使不处理不受检查异常也可以正常通过编译。是运行时的错误,例如除0会引发ArithmeticException(算术错误异常),此时程序崩溃并且无法恢复。RuntimeException及其子类都统称为非受检查异常。

Throwable类常用方法

public string getMessage()//返回异常发生时的简要描述。
public string toString()//返回异常发生时的详细信息。
public string getLocalizedMessage()//返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回结果相同。
public void printStackTrace()//在控制台上打印Throwable对象封装的异常信息。

try-catch-finally

try块:用于捕获异常,后面可以接零个或多个catch块,如果没有catch块,则必须跟一个finally块。

catch块:用于处理try捕获到的异常。

finally块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。

在下面三种特殊情况下,finally块不会被执行:

  • 在try或finally块中用了System.exit(int)退出程序。如果System.exit(int)在异常语句之后,finally还是会被执行。
  • 程序所在的线程死亡
  • 关闭CPU

注意:当try语句和finally语句中都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally与的返回值会覆盖原始的返回值,如下:

public class Test{
	public static int f(int value){
		try{
            return value*value;
        }finally{
            if(value == 2)
                return 0;
        }
	}
}

如果调用f(2),返回值将是0,因为try语句的返回值被finally语句的返回值覆盖。

使用try-with-resources来代替try-catch-finally

  1. 适用范围(资源的定义):任何实现java.lang.AutoCloseable或者java.io.Closeable的对象。
  2. 关闭资源和finally块的执行顺序:在try-with-resources语句中,任何catch或finally块在声明的资源关闭后运行。

《Effecitve Java》中明确指出:

面对必须要关闭的资源,我们总是应该优先使用try-with-resources而不是try-finally。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更简短清晰,产生的异常对我们更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally则几乎做不到这点。

Java中类似于InputStreamOutputStreamScannerPrintWriter等的资源都需要我们调用close()方法来手动的关闭。

使用try-catch-finally语句实现:

//读取文本文件的内容
Scanner scanner = null;
try{
	scanner = new Scanner(new File("D://read.txt"));
    while(scanner.hasNext())
    	System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
    e.printStackTrace();
}finally{
    if(scanner != null)
        scanner.close();
}

使用Java 7之后的try-with-resources语句改造上面的代码:

try (Scanner scanner = new Scanner(new File("D:\\read.txt"))){
	while(scanner.hasNext())
    	System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
    e.printStackTrace();
}

通过使用分号分隔符,可以在try-with-resources块中声明多个资源。

try(BufferredInputStream bin = new BufferredInputStream(new FileInputStream(new File("test.txt"))); BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))){
	int b;
    while((b = bin.read()) != -1)
        bout.write(b);
}catch (IOException e)
{
    e.printStackTrace();
}

十四、I/O流

posted @ 2021-07-16 15:54  Monste  阅读(275)  评论(0编辑  收藏  举报