文章仅作为复习作用
参考学习博主:<遇见狂神说>、<生命是有光的>
5、面向对象入门篇
5-1、类
类的定义
类的组成: 属性 和 行为
-
属性: 在代码中通过成员变量来体现(类中方法外的变量)
-
行为: 在代码中通过成员方法来体现(和前面的方法相比,去掉static关键字即可)
-
构造器(Constructor:初始化一个类的对象并返回引用)
-
代码块
-
内部类:(在一个A类中再定义一个B类,这个B类就称为内部类),
其中包括: 局部内部类、静态内部类、匿名内部类(重点) (即直接 new A().xxx() 这样来调用方法)
public class Student{
// 属性: 姓名,年龄
// 成员变量:跟之前定义变量的格式一样,只不过位置发生了改变,类中方法外
String name;
int age;
// 在不在Student 类中 ? 在
// 在不在方法外? 在,因为Student 类中没有方法
// 行为: 学习
// 成员方法:跟之前定义方法的格式一样,只不过去掉了static关键字
public void study(){
System.out.println("学习");
}
}
内部类代码示例
public class Fifth {
{
System.out.println("这是一个匿名的方法");
}
static{
System.out.println("这是一个静态代码块");
}
public Fifth(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Fifth fifth = new Fifth();
System.out.println("==========");
Fifth a = new Fifth();
}
//最后得到
/**这是一个静态代码块
* 这是一个匿名的方法
* 构造方法
* ============
* 这是一个匿名的方法
* 构造方法
*
* 所以一般类执行是 先执行静态代码块,再执行匿名方法,最后才执行构造方法
*
* 但是静态代码块只会执行一次
*/
}
5-2、创建对象
类与对象:
- 类是抽象的,所以需要对他进行实例化,即 new
- 类是对象的抽象:模板Class
- 对象是类的具体
- 关于一个类,即使它什么都不写,也会存在一个方法
创建对象
// 格式: 类名 对象名 = new 类名();
// Student s = new Student();
使用对象
// 1.使用成员变量
// 格式: 对象名.变量名
s.name
// 2.使用成员方法
// 格式: 对象名.方法名()
s.study();
————————————————-------------
public class TestStudent {
/*
创建对象的格式:
类名 对象名 = new 类名();
调用成员变量的格式:
对象名.变量名
调用成员方法的格式:
对象名.方法名();
*/
public static void main(String[] args) {
// 1.创建对象
TestStudent testStudent = new TestStudent();
System.out.println("请输入第一个数字");
Scanner scanner = new Scanner(System.in);
int a,b;
a=scanner.nextInt();
System.out.println("请输入第二个数字");
b=scanner.nextInt();
// 2.调用成员方法
System.out.println("最大的值为:"+testStudent.getMax(a,b));
scanner.close();
}
public int getMax(int a,int b){
if(a>b){
return a;
}else{
return b;
}
}
public static String getMax(String a){
return a;
}
}
成员变量和局部变量的区别
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中方法外 | 方法内或者方法声明上(形参) |
内存中位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的存在而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的调用完毕而消失 |
初始化值不同 | 有默认的初始化值 | 没有默认的初始化值,必须先定义,赋值,才能使用 |
5-3、构造方法
5-3-1、简介
构造方法:构建、创建对象的时候,所调用的方法
5-3-2、格式
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由return带回结果数据)
new 本质是在调用构造器
5-3-3、无参构造
- 无参构造:每个类中的开头都会存在一个无参构造,即 public xxx(){xx};
- 若在这个类中写了一个有参构造,即 public xxx(xxx){xx}; 那么这个无参构造就不存在了
IDEA快捷键:art+insert 即可定义一个无参构造或有参构造。
当然定义一个有参构造前你需要定义对应属性
- Part 1
public class Student { // 类
public Student(){ // 构造方法
System.out.println("我是Student类的构造方法");
}
}
- Part 2
// 调用方法
public class TestStudent {
public static void main(String[] args){
Student stu = new Student();
// 我是Student类的构造方法
}
}
// 执行时机
// 1.创建对象的时候调用,每创建一次对象,就会执行一次构造方法
// 2.不能手动调用构造方法
stu.Student(); // 报错
5-3-4、StringBuilder
方法名 | 说明 |
---|---|
public StringBuilder() | 创建一个空白可变字符串对象,不含有任何内容 |
public StringBuilder(String str) | 根据字符串的内容,来创建可变字符串对象 |
public class Demo {
public static void main(Srting[] args){
// public StringBuilder():创建一个空白可变字符串对象,不含有任何内容
StringBuilder sb = new StringBuilder();
System.out.println(sb); // 打印出来是空白
// public StringBuilder(String str): 根据字符串的内容,来创建可变字符串对象
StringBuilder sb2 = new StringBuilder("abc");
System.out.println(sb); // 打印出来 abc
}
}
注 :
1.如果没有定义构造方法,系统将给出一个默认的无参数构造方法
2.如果定义了构造方法,系统将不再提供默认的构造方法
解决方法:
无论是否使用,都手动书写无参数构造方法,和带参数构造方法
6、面向对象
面向对象三大特性:封装、继承、多态
6-1、封装 (数据的隐藏)
通常,应禁止访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
封装的核心:private
private是一个权限修饰符,可以用来修饰成员(变量,方法)
特点:被private修饰的成员只能在本类中才能访问
属性私有、get、set (alt+insert)
-
当你在public A方法中去new了一个private的对象,你需要在这个public方法中 xx.setxx(); 再通过 对象名.getXX() 获得
-
且私有的属性无法继承,只能通过get和set获取
**针对private修饰的常量及封装的意义 ** :
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统的可维护性增加了
- Part 1
package Demo04;
public class Student{
//属性私有
//名字
private String name;
//学号
private int id;
//性别
private String sex;
//年龄
private int age;
//get 获得这个数据
public String getName(){
return this.name;
}
//set 给这个数据设置值
public void setName(String name){ //注意这里是设置,即给值,所以需要形参
this.name=name;
}
//通过 art+insert快捷键 可以自动生成对应的get,set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>120 || age<0){
System.out.println("不合理");
this.age=10;
}else{
this.age=age;
}
}
}
- Part 2 调用
public class Demo1 {
public static void main(String[] args) {
String name;
int age;
Demo04.Student student = new Demo04.Student();
student.setName("周末");
System.out.println(student.getName());
student.setAge(-3);
System.out.println(student.getAge());
}
}
//运行Demo1 得到
周末
不合理
10
6-2、继承
不同类型的对象,相互之间经常有一定数量的共同点。
通过使用继承,子类可以快速地使用父类的方法,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
**继承关键:**extends (子类 extends 父类)
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。(以后介绍)。
6-2-1、关于类(温故而知新)
- 成员变量(Field:描述类和对象的属性信息的)
- 成员方法(Methpd:描述类或者对象的行为信息的):包括实例方法,静态方法,抽象方法,getter setter 方法
- 构造器(Constructor:初始化一个类的对象并返回引用)
- 默认无参构造器:一个类默认会自带一个无参构造器,即使不写它也存在,但是如果一个类它写了一个构造器,那么默认的无参构造器就被覆盖了!
- 有参构造器
- 代码块
- 内部类
6-2-2、this关键字
this
关键字可以用在实例方法和构造器中this
关键字用在方法中,谁调用这个方法,this
就代表谁this
关键字用在构造器上,代表了构造器正在初始化那个对象的引用
/*
javaBean类: 封装数据
*/
public class Student {
// 两个成员变量 私有
private String name;
private int age;
// 无参构造器
public Student(){
}
// 有参数构造器
public Student(String name,int age){
this.name = name;
this.age = age;
}
// 成员方法: setXX \ gerXX
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age
}
public int getAge(){
return age;
}
public void show(){
System.out.print;n(name + "..." + age);
}
}
6-2-3、static关键字
– 按照有无static
修饰,成员变量和方法可以分为
-
成员变量
-
静态成员变量(类变量)
有
static
修饰的成员变量称为静态成员变量,也叫类变量,属于类本身,直接用类名访问即可(xx.xx) -
实例成员变量
无
static
修饰的成员变量称为实例成员变量,属于类的每个对象的,必须用类的对象来访问(需要new对象后才能调用)
-
-
成员方法
-
静态方法
有
static
修饰的成员方法称为静态方法,也叫类方法,属于类本身的,直接用类名访问即可 -
实例方法
无
static
修饰的成员方法称为实例方法,属于类的每个对象的,必须用类的对象来访问
-
-
总结:成员变量的访问语法
- 静态成员变量访问:
- 类名.静态成员变量
- 对象.静态成员变量(也可以访问,但是不推荐)
- 实例成员变量的访问: 对象.实例成员变量
- 静态成员变量访问:
代码示例
public class Student {
// 1.静态成员变量: 有static修饰,属于类本身,直接用类名访问即可!
public static String schoolName = "中国";
// 2.实例成员变量: 无static修饰,属于类的对象,必须用对象访问
private String name;
private int age;
public static void main(String[] args){
// 1.类名.静态成员变量
System.out.println(Student.schoolName);
// 注意:同一个类中访问静态成员变量可以省略类名不写
System.out.println(schoolName);
// 2.对象.实例成员变量
Student s1 = new Student();
s1.name = "孙悟空";
System.out.println(s1.name);
// 3.对象.静态成员变量(不推荐)
System.out.println(s1.schoolName);
// 静态成员变量属于类,直接用类名访问即可
}
}
6-2-4、继承后成员变量的访问特点
就近原则:子类有就找子类,子类没有找父类,父类没有就报错!
this
代表了当前对象的引用,可以用于访问当前子类对象的成员变量super
代表了父类对象的引用,可以用于访问父类中的成员变量
public class TestDemo {
public static void main(String[] args) {
Cat cat = new Cat();
cat.show();
}
}
class Animal{
public String name = "动物名称";
}
class Cat extends Animal {
public String name = "子类名称";
public void show() {
String name = "局部名称";
System.out.println(name); //局部名称
System.out.println(this.name); //子类名称
System.out.println(super.name); //父类名称
}
}
6-2-5、继承后成员方法的访问特点
- 子类对象优先使用子类已有的方法
6-2-6、继承后方法的重写
- java建议在重写的方法上面加一个
@override
注解 - 方法一旦加了这个
@override
注解,那就必须是成功重写父类的方法,否则报错!
具体方法的重写:可参考方法篇重写与重载的区别部分
6-2-7、继承后构造器的特点
- 子类继承父类,子类的全部构造器默认会先访问父类的无参构造器,再执行子类自己的构造器
为什么子类的构造器会先调用父类的构造器?
答:1. 子类的构造器的第一行默认有一个super()
调用父类的无参构造器,写不写都存在
2.当我们调用子类构造器初始化子类对象数据的时候,必须先调用父类构造器初始化继承自父类的属性和行为(先有爸爸,后有儿子)
6-2-8、this 与 super
this
代表了当前对象的引用(继承中指代子类对象)this.子类成员变量
this.子类成员方法
this(....):可以根据参数匹配访问本类中其他构造器
super
代表了父类对象的引用(继承中指代了父类对象空间)super.父类成员变量
super.父类的成员方法
super(....):可以根据参数匹配访问父类的构造器
注意:
this(...)
借用本类其他构造器super(...)
调用父类的构造器this(...)和super(...)
必须放在构造器的第一行,否则报错- 所以
this(...)
和super(...)
不能同时出现在构造器中!!
区别:
类别 | this | super |
---|---|---|
代表的对象 | 本身调用者的这个对象 | 代表父类对象的应用 |
使用前提 | 没有继承也可以使用 | 只能在继承条件才可以使用 |
构造方法 | 本类的构造 | 父类的构造 |
若还不理解可回顾看视频 :<狂神说JAVA>—面向对象10:super详解
6-3、针对面试常考
1、八种访问形式问答
-
实例方法是否可以直接访问实例成员变量?
答:可以的,因为它们都属于对象
-
实例方法是否可以直接访问静态成员变量?
答:可以的,静态成员变量可以被共享访问
-
实例方法是否可以直接访问实例方法?
答:可以的,实例方法和实例方法都属于对象
-
实例方法是否可以直接访问静态方法?
答:可以的,静态方法可以被共享访问
-
静态方法是否可以直接访问实例变量?
答:不可以,静态方法属于类,实例变量属于对象,实例变量必须用对象访问
-
静态方法是否可以直接访问静态变量?
答:可以的,静态成员变量可以被共享访问
-
静态方法是否可以直接访问实例方法?
答:不可以,实例方法必须被对象访问
-
静态方法是否可以直接访问静态方法?
答:可以的,静态方法可以被共享访问
2、继承
-
子类中不能继承父类东西是什么?
答:子类不能继承父类的构造器,子类有自己的构造器
-
子类是否可以继承父类的私有成员(私有成员变量,私有成员方法)?
答:有争议,我认为子类是可以继承父类的私有成员的,只是不能直接访问
-
子类上是否可以继承父类的静态成员?
答:有争议,我认为子类是不能继承父类的静态成员,但静态方法是可以继承的
3、重写
静态方法和私有方法是否可以被重写? —>答:不可以,加上@Override会报错
4、继承后构造器特点
为什么子类的构造器会调用父类的构造器? —>答:子类的构造器第一行默认有super()
调用父类的无参构造器,写不写都存在
5、JAVA是单继承
利用反证法即可得到结论
// 假如Java可以多继承,请看如下代码
class A{
// 成员方法
public void test(){
System.out.println("A");
}
}
class B{
// 成员方法
public void test(){
System.out.println("B");
}
}
class C extends A,B{
public static void main(String[] args){
C c = new C();
c.test(); //出现了类的二义性!所以Java不能多继承!!
}
}
6-4、多态
简介:同一个类型的对象,执行同一个行为,在不同状态下会表现出不同的行为方式
枯燥的概念:
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间(代码在跑的时候)才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
若开始并不理解,可以结合代码来理解
6-4-1、多态的形式
- 父类类型 对象名称 = new 子类构造器
Father A =new Son()
- 接口 对象名称 = new 实现类构造器
- 父类类型范围 > 子类类型范围
public class PolymorphicDemo {
public static void main(String[] args) {
//父类类型 对象名称 = new 子类构造器;
Person dd = new Student();
dd.run(); //对于方法的调用: 编译(写代码)看左边Person,运行看右边 Student
//看左边指的是看等号的左边,等号左边是Person,会找Person里面的run方法,不报错
//运行的时候看等号的右边,等号右边是Student,执行Student里面的run方法
// 学生跑的飞快~~~
System.out.println(dlam.name); //对于变量的调用: 编译看左边,运行看左边
//人名称Person
Person tt = new Teacher();
tt.run(); //对于方法的调用: 编译(写代码)看左边,运行看右边
//老师跑的飞快~~~
System.out.println(taiDi.name); //对于变量的调用: 编译看左边,运行看左边
//人名称Person
}
}
class Student extends Person{
public String name = "人的名称Student";
@Override
public void run(){
System.out.println("学生跑的飞快~~~");
}
}
class Teacher extends Person{
public String name = "人的名称Teacher";
@Override
public void run(){
System.out.println("老师跑的飞快~~~");
}
}
class Person{
public String name = "人名称Person";
public void run(){
System.out.println("人跑的飞快~~~");
}
}
6-4-2、多态的使用
多态是方法的多态,属性没有多态!
前提:
- 必须存在继承或者实现关系
- 必须存在父类类型的变量引用(指向)子类类型的对象
- 需要存在方法重写
**注意:**其实从上面的代码可以看出来,当实现了 Father A =new Son()
这个多态形态后,子类重写了父类的方法,那当调用这个方法的时候,程序运行是走子类的方法,所以才会有 编译(写代码)看左边,运行看右边
6-4-3、多态的优劣势
- 优势
- 在多态形式下,右边对象可以实现组件化切换,业务功能也随之改变,便于扩展和维护。可以实现类与类之间的解耦
- 实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,可以传入一切子类对象进行方法的调用,更能体现出多态的扩展性与便利
- 劣势
- 多态形式下,不能直接调用子类特有的功能。(编译看左边!!)
- 因为父类中没有子类独有的功能,所以代码在编译阶段就直接报错了
6-4-4、引用类型自动类型转换(向上转型)
基本数据类型的转换:
- 小范围类型的变量或者值可以直接赋值给大范围类型的变量
- 大范围类型的变量或者值必须强制类型转换给小范围类型的变量
引用数据类型转换的思想是一样的:
- 父类类型的范围 > 子类类型的范围
- Person>Student
- 子类类型的对象或者变量可以自动类型转换赋值给父类类型的变量
Person qq=new Student();
6-4-5、引用类型的强制类型转换(向下转型,可能会丢失一些方法,不是很好)
-
父类类型的变量或者对象必须强制类型转换成子类类型的变量,否则报错!
-
强制类型转换的格式:
-
类型 变量名称 = (类型)(对象或者变量)
-
Person dd=new Student(); //把人的类型变量dd转换为 Student cc=(Student)dd;
-
6-4-6、instanceof关键字(了解即可)
前提:B extends A ;C extends A
- 首先 A a =new B(); 然后 看的是A与B的关系(父子关系)
- Systemui.out.print(a instanceof B); 如果他们之间有关系,则会得到true
- Systemui.out.print(a instanceof C); 因为B和C没有关系,所以会得到false
6-5、内部类
**定义:**定义在一个类里面的类就是内部类
**内部类的作用:**可以提供更好的封装性,内部类有更多的权限修饰符,封装性有了更多的控制
内部类的分类:
- 静态内部类
- 实例内部类(成员内部类)
- 局部内部类
- 匿名内部类(重点)
6-5-1、静态内部类(了解)
定义:有static
修饰,属于外部类本身,会加载一次
静态内部类的访问格式: 外部类名称.内部类名称
public class InnerClass {
public static void main(String[] args) {
//外部类名称.内部类名称 对象名称 = new 外部类名称.内部类构造器
Outter.Inner in = new Outter.Inner();
in.setName("张三");
in.setAge(12);
in.show();
}
}
class Outter{
private double salary;
public static int age1 = 15;
// 静态内部类: 有static修饰,属于外部类本身,只会加载一次
public static class Inner{
private String name;
private int age;
public Inner() {
}
public Inner(String name, int age) {
this.name = name;
this.age = age;
}
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 show() {
System.out.println(name +"是" + age + "岁");
System.out.println(age1);
// 15
// System.out.println(salary); 报错
}
}
}
6-5-2、实例内部类(了解)
定义:无static
修饰的内部类,属于外部类的每个对象,跟着对象一起加载
实例内部类的访问格式:外部类名称.内部类名称
public class InnerClass {
public static void main(String[] args) {
// 实例内部类属于外部类对象。实例内部类的宿主是外部类对象!
Outter.Inner in = new Outter().new Inner();
in.show();
}
}
class Outter{
public static int age = 11;
private String salary;
// 实例内部类: 无static 修饰,属于外部类的对象
public class Inner{
private String name;
// 不能在实例内部类中定义静态成员!!
// public static String schoolName; 报错
// 可以定义常量
public static final String schoolName = "黑马";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 实例方法
public void show(){
System.out.println(age);
System.out.println(salary);
}
}
}
6-5-3、局部内部类(了解)
定义:在方法,构造器,代码块,for循环中定义的内部类,就是局部内部类
成分:只能定义实例成员,不能定义静态成员,可以定义常量
局部内部类基本没啥用!
public class InnerClass {
static {
// 局部内部类
abstract class A{
}
}
public static void main(String[] args) {
// 局部内部类
class A{
}
}
public static void test(){
// 局部内部类
class Animal{
}
// 局部内部类
class A extends Animal{
}
}
}
6-5-4、匿名内部类(重点)
定义:就是一个没有名字的局部内部类
作用:匿名内部类可以简化代码,是开发中常用的形式
格式:
new 类名|抽象类|接口(形参){
方法重写
}
匿名内部类的特点:
1.匿名内部类是一个没有名字的内部类
2.匿名内部类一旦写出来,就会立即创建一个匿名内部类的对象返回
3.匿名内部类的对象的类型相当于是当前 new 的那个类型的子类类型
public class People{
public static void main(String[] args) {
Person a = new Person() {
@Override
public void run() {
System.out.println("人跑的贼快~~~");
}
};
a.run();
a.go();
}
}
abstract class Animal{
public abstract void run();
public void go(){
System.out.println("开始go~~~");
}
}