Java_02 面向对象:类与对象,变量,封装,继承,多态,抽象类,接口,异常
面向对象
Java面向对象:一种组件化的设计思想,万物皆对象
c:面向过程;c++:半面向对象
定义:将我们客观世界中的所有事物全部描述成对象,并通过抽象的思维方式将需要解决的实际问题分解成人们利于理解的对象模型,然后通过这些模型来构架应用程序功能。
面向对象最重要的一点是抽象的技术
面向对象跟符合人们说话办事的思维方式,可以把复杂的事情简单化
类与对象
类的定义:- Java中我们的每一个class文件就代表一个类
-具有共同属性的对象可以成为类,类是一个抽象的概念
对象:- 日常生活中所有的东西都是对象,对象是类的实例化表现
属性:- 用来描述对象的特征,例如张三的名字叫张三
方法:-代表的是每一个对象的一些行为,例如猫会吃饭
简单的说:我们可以把类看成汽车的设计图纸,根据图纸可以生产出无数的汽车;我们也可以通过类产出无数对象,对象可以拥有类的一切信息,而类不能有对象的一切信息
思考:为什么需要方法?
答:可以把简单的代码抽离出来,以后使用的时候直接调用即可。
public int sum(int a, int b){return a+b ;}
访问修饰符 返回的数据类型 方法名(参数列表){方法体,有返回值则return;}
-
返回值类型:当方法执行完成后,想返回给调用者什么数据就用什么数据类型,如果方法执行完成后不需要返回值,就用void修饰,而如果有就用return把数据返回。
-
方法名:自己定义,见名知意,符合命名规则
-
参数列表:当别人调用方法时需要传递给方法的数据,需要什么类型数据就定义什么类型,类型后跟上变量名即可,中间用逗号隔开
访问修饰符的区别: public:完全公开,被其修饰代表任意包下的任意类都可以访问 protected:受保护的,只有同包及子类包可以访问,同包直接访问,不同包的子类必须通过子类对象访问 default:无访问修饰符,只有本包可以访问 private:私有的,只有本类中可以访问 -
方法的种类:
-
类(静态)方法:public 后跟static就是类方法,类方法属于类本身,可以直接通过类名.方法名();调用
-
实例方法:[访问修饰符] 返回值 方法名([参数类型 参数名...])
类方法与实例方法的区别:
-
调用:类方法可以用类名直接调用,而实例方法必须先创建对象才可以调用
-
实例对象可以调用类方法,但是类不能调用实例方法
-
实例对象可以使用类变量,但是类方法不能使用实例变量
Java内存划分:
-
栈内存:基本数据类型的值;引用数据类型的变量名(引用地址)
-
堆内存:通过new关键字产生的对象
方法区:保存所有编译过后的class信息;类方法,类变量,实例方法的名字
-
-
构造方法:
- 是给类创建对象的时候调用的方法,用于创建对象,无返回值,如果没有在类中声明构造方法,那么 JDK 会给我们提供默认的构造方法
- 注意:构造方法的方法名必须与类名一致;
- 构造方法不能被static修饰;
- 创建一个对象构造方法只执行一次;
- 有参构造用来给对象初始化赋值;
- 当我们显示声明构造方法时(有参或无参),系统都不会给我们提供默认构造了,所以我们在声明有参构造后,要一起声明无参。
-
方法重载:
定义:在同类中,方法名一致,但是参数类型、参数个数不同叫方法重载,构造方法也可以被重载
重点:方法重载时Java编译时多态的一个重要体现,调用同样的方法,但是传递的参数不同,执行不同的操作。
-
代码块
- 普通代码块,定义在方法中{},不加任何修饰符就是,一般较少使用,经常用于方法过长或变量名冲突
- 构造块,定义在类中{},不加任何修饰符,构造块优先执行,每创建一个对象,构造块就执行一次,可以执行一些简单的逻辑操作,如打印日志
- 静态块,定义在类中,被static修饰,执行优于构造块,但只执行一次,如果某些属性需要在创建对象前处理,可以使用
- 同步代码块,synchronized修饰,只允许一个线程执行
执行顺序:静态块 > 构造块 > 构造方法
package com.alpari;
public class Hello {
{
System.out.println("构造块");
}
static {
System.out.println("静态块");
}
public static void main(String[] args) {
new Hello();
System.out.println("-------------");
new Hello();
}
}
// 输出:
静态块
构造块
-------------
构造块
变量
变量的种类:
-
类变量(全局变量):直接声明在类中,并被static修饰来制定这个变量属于类,其作用域在整个类中,变量声明后初始值为其数据类型的默认值
-
实例变量(全局变量):直接声明在类中(不能再方法中),不需要static修饰,其作用域在类的所有实例方法中,初始值是其类型的默认值
package com.alpari; public class Hello { static int b;//类变量 int c;//实例变量 final int A = 2;//常量 public static void main(String[] args) { for (int i =0; i<10; i++) { int d = 3;//局部变量 } } } -
局部变量:只要没声明在类中,例如:for循环中等,其作用域在定义变量的对应作用域,相同作用域内变量名不能重复,变量声明后没有初始值,所以必须赋值后才能使用
-
常量:使用final修饰,常量的值不会修改,所以在创建时必须赋值,常量的命名规则所有字母全部大写,多个单词用_分割,可以被static修饰,也可以不用
类变量与实例变量的区别:
- 一个被static修饰,一个没有
- 类变量可以通过类名直接调用,而实例变量必须通过对象名调用
- 属于类的变量只有一份存放在Java内存中的方法区,不能创建多个,而实例变量存放在Java内存的堆内存中,new多少个实力就有多少个对应变量
- 类变量作用于整个类的所有方法中,而实例变量只能作用于实例方法
为什么类变量可以作用于实例方法中,而实例变量不能作用于类方法中?
答:由于一个类的实例对象会拥有类的全部属性和方法,所以实例方法中可以使用类属性,由于实例是根据类生成的,先有的类才有实例对象,所以类方法无法使用实例变量
封装
目的:保护内部定义结构的安全性
1、把一个类的属性及行为抽象到一起,例如:Pig类应该只有Pig的属性以及行为,不应该有Dog的
2、把类的属性私有化private,提供公开的方法访问,setXxx()与getXxx()
3、对于一些不想让人调用的方法用private修饰
package com.alpari;
public class Dog {
private String name; // 属性私有化
public int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Test{
public static void main(String[] args) {
Dog dog = new Dog();
dog.age = 2; //public可以直接赋值
dog.setName("22"); //private只能通过提供的方法赋值
}
}
-
this关键字
- 代表当前对象,如this.name 代表的是当前对象的name
- 当调用的是哪个对象,this就代表哪一个对象
- 我们可以使用this.属性调用当前对象的属性,this.方法调用方法
- 我们可以使用this()调用无参构造,也可以通过this(参数)调用有参,但是使用this关键字调用构造方法的时候,构造方法必须放在第一行,构造方法不能循环调用
-
Java中值传递
-
值传递
-
基本数据类型传递及常量池String的传递
当我们定义的基本数据类型的变量传递到方法中后,方法会自动给我们的变量创建一个副本,然后在方法中操作的是副本的值,而不会影响我们在外面声明的变量值
package com.alpari; public class Dog { public static void main(String[] args) { int a = 2; new Dog().sum(a); System.out.println("执行完sum------"+a); } public void sum (int a) { a ++; System.out.println("这是sum中的:"+a); } } //输出: 这是sum中的:3 执行完sum------2 -
引用数据类型的传递
传递引用数据类型的时候如果把地址传过去,name由于双方地址指向的是同一堆内存,所以只要任意一方修改了数据,那么另一方的数据会同时产生变化
-
-
-
- Java中不存在引用传递
继承
就是在已有的程序结构上继续扩充新功能,一个类可以从现有的类派生类,这样的过程叫继承,新类被称为子类,现有的类被称为父类,子类将继承父类的属性及方法
-
继承关键字extends,我们可以把一些共有的属性及行为抽取出来,然后子类继承即可拥有;
-
子类无法继承父类的私有属性及方法
-
子类无法继承父类的构造方法
-
Java中只支持单继承
-
Java中所有的类都有父类,没声明默认是Object,叫做超类
package com.alpari; // 父类 public class Animal { public void eat(){ System.out.println("这是父类的eat"); } public void say(){ System.out.println("这是父类的say"); } }package com.alpari; public class Dog extends Animal { @Override // 代表重写 public void say() { System.out.println("不满意父类的方法,自己的say"); } public static void main(String[] args) { new Dog().eat(); new Dog().say(); } } // 输出: 这是父类的eat 不满意父类的方法,自己的say -
重写:
子类可以继承父类的方法,如果对父类的方法不满意,可以重写父类的方法,对其功能进行扩展,当子类重写父类的方法,默认调用子类的方法实现
注意点:
- 访问修饰符必须与父类相同或大于父类
- 返回值类型必须相同
- 方法名必须相同
- 参数列表要求一致
- 子类可以拥有自己的属性及方法,子类重写父类方法属性后,可以使用super.方法名();调用父类被隐藏的方法
- 在创建子类对象前,会先调用父类的无参构造创建父类对象,如果父类没有无参构造,需要子类在构造方法中使用super(参数)调用父类的有参构造
- 重写是Java运行时的多态
-
final关键字
修饰在变量上叫常量;修饰在类上,代表这个类不能被继承,如String类;修饰在方法上,代表这个方法不能被重写
多态
多态就是指调用相同的方法,但根据传入的参数不同,或顺序不同执行不同的操作:发送消息给某个对象,让对象自行决定采用哪种行为来响应消息子类对象的引用赋值给了父类引用变量来实现动态方法的调用
形成多态的前提:继承,实现,方法重载,方法重写,向上转型
基于重载:调用同样的方法名,但是根据传递的参数不同却执行不同的操作
基于重写:通过java对象的向上自动转型,把子类对象的实例引用赋值给父类的声明,然后实现虽然我们声明的是父类,但根据实例化子类的不同,调用相同的方法却执行不同的操作
向上转型:父类的引用指向了子类的实际对象,或子类的对象赋值给了父类的引用,自动完成
package com.alpari;
public class Dog extends Animal {
@Override // 代表重写
public void say() {
System.out.println("不满意父类的方法,自己的say");
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
Animal dog2 = new Dog();
dog2.say();
}
}
// 输出
不满意父类的方法,自己的say
不满意父类的方法,自己的say
向上转型的用处:完成数据类型的统一
向上转型的缺点:当我们子类向上转型后,会失去子类特有的方法,如果想使用子类特有的方法,需要对其向下转型
instanceof关键字
a instanceof b 来判断a是否为b类的对象,是返回true,否返回false; 来判断其是否属于这个类型,如果是则进行向下转型操作,否则不进行
抽象类
-
抽象类实际上也是一个类,被abstract修饰
-
抽象类可以拥有已实现及未实现方法,未实现方法需要被abstract修饰
-
由于抽象类中肯定存在未实现的方法,所以抽象类不能实例化对象
-
抽象类也是类,所以可以使用子类继承抽象类,然后实例化子类对象赋值给父类的引用
package com.alpari; // 父类抽象类 public abstract class Animal { public abstract void eat(); // 只有一个抽象方法 } ====================================== package com.alpari; public class Dog extends Animal { @Override public void eat() { System.out.println("这是子类的eat方法"); } } class Test{ public static void main(String[] args) { Animal a = new Dog(); a.eat(); } } // 输出: 这是子类的eat方法 -
抽象类虽然不能实例化对象,但是也有构造方法的存在,在实例化子类对象的同时也需要先调用父类的构造方法
-
抽象类不能被final修饰,因为程序认为抽象类中肯定包含抽象方法,而抽象方法必须依靠子类去继承实现,但如果一个类被final修饰的话,那这个类就不能被继承
-
抽象类可以拥有静态方法,并且可以直接通过类名.去调用
abstract class A{ public static void eat(){ System.out.println("吃吃吃"); } } public class Test { public static void main(String[] args) { A.eat(); } } -
外部抽象类不能被static修饰,内部抽象类可以被static修饰,继承的时候使用外部类名.内部类名
package com.alpari; abstract class Animal{ public static void eat(){ System.out.println("吃吃吃"); } public abstract void ha(); static abstract class B{ public abstract void print(); } } class C extends Animal.B{ public void print() { System.out.println("C的print方法"); } } class Test1 { public static void main(String[] args) { Animal.B a=new C(); a.print(); } } -
构造方法是整个构造过程的最后一步,所有的属性设置都是在构造完成之后才会把值设置上去,在类的构造方法完成前,所有的属性都只是其类型的默认值,因为执行子类构造方法之前需要执行父类构造方法,在父类构造方法中调用子类的方法时,由于还没完成子类构造,所以num=0
package com.alpari; abstract class Animal{ public Animal(){ //2:执行父类构造 this.print(); //3:调用print方法 } public abstract void print(); } class B extends Animal{ private int num=100; public B(int num){ //5.num传递过来执行子类构造方法结束后num=50 this.num=num; } public void print() { //4:调用覆写后的方法 System.out.println("num="+num); } } class Test1 { public static void main(String[] args) { //new B(50); //1:执行构造 new B(50).print(); } } // 输出 num=0 num=50 -
抽象方法不能被final,static,private修饰,因为抽象方法需要被子类重写,所以不能被final和private修饰,因为抽象方法没有方法体,所以无法通过类名直接调用,所以不能被static修饰
-
内部类调用外部类的属性:外部类.this.属性
-
内部类的实例化对象的创建:外部类.内部类 对象 = new 外部类().new 内部类();
接口
一个统一规定双方互相协调的规范,是在抽象类的基础上更深层次的抽象
-
接口的声明:使用interface,public interface 接口名 { };
-
主要为了解决java中单继承局限,用关键字implement来实现,先继承后实现
-
抽象类可以有普通方法,但接口中不可以有普通方法,JDK8可以用default修饰普通方法
package com.alpari; public interface IDao { default void add(){ System.out.println("---"); } } class IDaoImpl implements IDao{ public static void main(String[] args) { IDao dao = new IDaoImpl(); dao.add(); } } -
所有方法默认被public abstract修饰,所有常量默认被public static final修饰
异常
package com.alpari;
public class Hello {
public static void main(String[] args) {
try {
int a = 1/0;
}catch (Exception e) {
System.out.println("Exception异常");
}catch (Throwable e) {
System.out.println("Throwable异常"); // 最大的异常要在最后
} finally {
System.out.println("finally执行"); // 都会执行
}
}
}
-
异常的处理流程:
1、当程序运行时出现异常,会由JVM自动根据异常实例化一个对应的实例对象
2、对象产生完,会判断我们当前语句是否存在异常处理,若没有则由JVM输出异常自信心结束程序
3、若存在异常处理,那么由try来捕获异常实例对象,然后由catch来匹配,如果catch到了,则由当前catch处理
4、不管有没有匹配到,都向后执行,如果存在finally则执行其中代码,执行完后根据前面catch到的来处理,如果成功捕获,则继续执行finally之后的代码,反之由JVM处理
-
return与finally谁先执行
在执行的时候,是return语句先把返回值写到内存中,然后停下来执行finally,等finally执行完毕,return再去执行后面一段
-
什么情况下finally不执行:
1、在执行try之前return 则不会执行finally代码
2、在try中调用System.exit();
3、断电死机
-
Exception与RuntimeException的区别:
1、Exception是RuntimeException的父类
2、使用Exception定义的异常必须处理(编译型异常),而使用RuntimeException定义的可以选择性处理(运行时异常)
-
ArithmeticException:算数异常
-
NumberFormatException:数据转换异常
-
ArrayIndexOutOfBoundsException:数组索引越界异常
-
NullPointerException:空指针异常
-
-
Throw与Throws:
都不解决异常,throw:手动抛异常;throws:在方法之上抛异常,写在方法参数列表的括号后,加上需要抛出的异常名即可
-
自定义异常:
如果需要编译型异常就继承Exception,如果要运行时异常就继承RuntimeExcepion即可。

浙公网安备 33010602011771号