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
- 修饰变量:修饰变量时必须赋初值且不呢改变,修饰引用变量不能再指向其他对象。
- 修饰方法:方法前面加上final关键字,代表这个方法不可被子类重写。
- 修饰类:表示这个类不能被继承,类中的成员可以根据需要设为final,final类中的所有成员方法都会被隐饰地指定为final方法。
注:类的private方法会被隐饰地指定为final
protected:继承的子类可以对基类(父类)有完全访问权。对子类公开,不对其它类公开。
五、封装
每个对象都包含它能进行操作所需的所有信息,因此对象不必依赖其它对象来完成自己的操作,能够避免对象属性赋值的随意性。
封装的好处:
- 良好的封装能减少耦合(相互作用相互影响的关系)。
- 类内部的实现可以自由地修改。
- 具有清晰的对外接口。(对外的属性和方法)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只支持单继承,不允许多继承。
子类继承父类:
- 子类拥有父类所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类无法访问,只是拥有。
- 子类拥有自己的属性和方法,即子类可以在父类的基础上做扩展。
- 子类可以用自己的方式重写父类的方法。
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关键字
- super用于在子类调用父类的重名方法(不重名也能引用)。
- super在子类中引用重名的变量(不重名也能引用)。
- super()可以用于直接调用父类的构造方法。
继承的优点
继承使得所有子类公共的的部分都放在了父类,使得代码得到了共享,就可以避免重复,继承使得修改和扩展继承而来的实现都较为容易。
继承的缺点
继承也是有缺点的,那就是父类做出修改,子类就不得不进行改变。另外,继承也会破坏包装,父类实现的细节暴露给子类。
总结:只有合理的应用继承才能发挥好的作用。例如猫继承动物,而动物不能继承猫。
七、多态的浅理解
多态表示不同的对象执行相同的操作,但是要通过自己的实现代码来执行。
注意点:
- 子类以父类的身份出现。
- 子类在工作时以自己的方式来实现。
- 子类以父类身份出现时,子类特有的属性和方法不可以使用,只能用父类存在的方法。
多态有两种情形
编译时多态:
- 重载(overload)多个同名的不同方法。
运行时多态:
- 覆盖(override),子类对父类方法进行覆盖(重写)。
- 动态绑定--也称为虚方法调用,正真的方法在运行时才确定。
- 在调用方法时,程序会正确地调用子类对象的方法。
多态中成员的特点
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();
}
抽象类注意点:
- 抽象类不能实例化。
- 抽象方法必须被子类重写。
- 如果类中有抽象方法,那么类就必须定义为抽象类,不论是否还包含其它的一般方法。
让抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。
抽象类通常代表一个抽象的概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所有在一个继承关系形成的等级结构里,树叶节点应当是具体类。而树叶节点均应当是抽象类。
九、接口(interface)
接口就是把隐式公共方法和属性组合起来,以封装特定功能的一个集合,一旦类实现了接口,类就可以职称接口所指定的所有属性和成员,声明接口的语法和抽象类完全相同,但不允许提供接口中任何成员的执行方式。所以接口不能实例化,不能有构造方法和字段;不能有修饰符(如public,private等);不能声明虚拟的或静态的等。还有实现接口的类就必须要属性接口中的所有方法和属性。
一个类可以支持多个接口,多个类也可以支持相同的接口。接口的命名,前面要加一个大写的字母'I',这是规范。
接口用interface声明,而不是class,接口名称前要加‘I’,接口中的方法或属性前面不能有修饰符、方法没有方法体。
//声明一个IPeople接口,此接口有一个printInfo方法
interface IPeople{
void printInfo();
}
使用接口实现学生类
class Student implements IPeople{
public void printInfo(){
System.out.println("运行正确。");
}
}
区分抽象类和接口
抽象类可以给出一些成员的实现,接口却不包含成员的实现,抽象类的抽象成员可被子类部分实现,接口的成员需要实现类完全实现,一个类只能继承一个抽象类,但可实现多个接口等。
区分:
- 类是对对象的抽象;抽象类是对类的抽象;接口是对行为的抽象。
- 如果行为跨越不同类的对象,可使用接口;对于一些相似的类对象,用继承抽象类。(实现接口和继承抽象类并不冲突)。
- 从设计者角度来讲,抽象类是从子类对象中发现了公共的东西,泛化出父类,然后子类继承父类。而接口是根本不知子类的存在,方法如何实现还不确认,提前定义。
十、泛型
泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所储存或使用的一个或多个类型的占位符。泛型集合类可以将类型参数用作它所存储的对像的类型的占位符;类型参数作为其字段和其方法的参数类型出现。
IList arrayPeople;//声明一个集合变量,可以用接口IList,也可以直接声明“ArrayList arrayPeople;”
用法就是在IList和List后面加'
//声明一泛型集合变量,用接口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
- 适用范围(资源的定义):任何实现java.lang.AutoCloseable或者java.io.Closeable的对象。
- 关闭资源和finally块的执行顺序:在try-with-resources语句中,任何catch或finally块在声明的资源关闭后运行。
《Effecitve Java》中明确指出:
面对必须要关闭的资源,我们总是应该优先使用try-with-resources而不是try-finally。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更简短清晰,产生的异常对我们更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally则几乎做不到这点。
Java中类似于InputStream、OutputStream、Scanner、PrintWriter等的资源都需要我们调用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();
}