Java(三)面向对象的特性
构造方法生成小技巧:使用idea时,在代码空白处单击右键,选择Generate选项,之后再选择Constructor生成构造器,也可以使用
toString()
来重写这个函数。
继承
- 父类也称为超类、基类、派生类等
- Java中只有单继承,没有像C++那样的多继承,多继承会引起混乱,使得继承过于复杂,系统难于维护
- Java中没有多继承,但接口有多继承
- 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问(比如:父类私有属性和方法)
- 如果定义一个类时,没有调用extends,则它的父类是:
java.lang.Objetct
package cn.yhq.oo; //非注释语句第一句
public class User {
public static void main(String[] args) {
Student stu1 = new Student(20,"自动化","yhq","体育课");
stu1.learning();
stu1.showMajor();
}
}
class Person{
int age;
String name;
String major;
//父类中不能含有参构造,如果要继承的话
// Person(int age,String name,String major){
// this.name = name;
// this.age = age;
// this.major = major;
// }
public void showMajor(){
System.out.println("我的专业是"+major);
}
}
class Student extends Person{
String lesson;
public Student(int age,String major,String name,String lesson){
this.major = major;
this.name = name;
this.age = age;
this.lesson = lesson;
}
public void learning(){
System.out.println("我在上课呢"+name+lesson);
}
}
可以使用
idea
的功能可以方便查看类的继承结构
instanceof
方法
使用instanceof
可以判断对象是否归属于某个类:
Student stu1 = new Student(20,"自动化","yhq","体育课");
System.out.println(stu1 instanceof Person);
System.out.println(stu1 instanceof Object);
System.out.println(new Person() instanceof Object);
方法重写(override)
子类通过重写父类的方法,可以使用自身的行为替换父类的行为。 方法重写需要符合下面三个要点:
- “==“:方法名、形参列表相同
- “<=”:返回值类型和声明异常类型,子类小于等于父类
- ”>=“:访问权限,子类大于等于父类
class Vehicle{
int speed;
public void showTheType(){
System.out.println("嗡嗡嗡");
}
public Person whoIsPsg(){//父类类型为Person,子类的类型要小于Person
return new Person();
}
}
class Horse extends Vehicle{
public void showTheType() {
System.out.println("马叫");
}
public Vehicle method(){
return new Vehicle();
}
public Person whoIsPsg(){ //返回值类型小于父类的类型,如果是Object就会报错
return new Student();
}
}
Object类
object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。其中toString()
是用于返回以一个字符串表示的对象值,是一种自我描述的方法,一般通过重写来打印所需要的内容。
public class TestObject {
public static void main(String[] args) {
TestObject to = new TestObject();
System.out.println(to.toString());
Person2 p2 = new Person2("喜喜",6);
System.out.println(p2.toString());
}
public String toString(){
return "Test Obeject 对象";
}
}
class Person2{
String name;
int age;
public String toString(){
return name+"年龄"+age;
}
public Person2(String name,int age){
this.name = name;
this.age = age;
}
}
==
和equals方法
==
代表比较双方是否相同,如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。
Object类中定义有:public boolean equals(Object obj)
方法,提供定义"对象内容相等"的逻辑。equals的源代码为:
public boolean equals(Object obj) {
return (this == obj);
}
重写equals代码:
package cn.yhq.oo;
import java.util.Objects;
public class TestEquals {
public static void main(String[] args) {
Object obj;
String str;
User03 u1 = new User03("yhq","123456");
User03 u2 = new User03("yhq","123456");
System.out.println(u1==u2);//这表示不是同一个对象,结果为false
System.out.println(u1.equals(u2));//判断两个对象是否相同时吗,通常是判断其id是否相同
}
}
class User03{
String username;
String pwd;
public User03(String username, String pwd) {
this.username = username;
this.pwd = pwd;
}
public String toString() {
return "User03{" +
"username='" + username + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User03 user03 = (User03) o;
return Objects.equals(username, user03.username);
}
}
super关键字
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。构造方法的第一句总是:super(…)来调用父类对应的构造方法。所以流程是:
1.先向上追溯到Object
2.然后再依次向下执行类的初始化块和构造方法
3.执行到当前子类位置
静态初始化块调用顺序和构造方法调用顺序一致,不再重复
package cn.yhq.oo;
public class TestSuper {
public static void main(String[] args) {
SonClass son = new SonClass();
son.work();
son.superWork();
}
}
class FatherClass{
String name;
int age;
FatherClass(){
System.out.println("fatherClass");
}
void work(){
System.out.println("father work");
}
}
class SonClass extends FatherClass{
//每个类的第一句话都是super(),自动调用父类的构造函数
SonClass(){
System.out.println("SonClass");
}
void work(){
System.out.println("Son work");
}
void superWork(){
super.work();
}
}
封装
类的定义规则
1.一般使用private访问权限。
2.提供相应的set、get方法来访问相关的属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作。
3.一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
package Test;
//成员变量全部换位public
public class Person4 {
private int id;
private String name;
private int age;
private boolean man;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
if (age>=1&&age<=130){
this.age = age;
}else {
System.out.println("请输入正常年龄");
}
}
}
类中只包含set和get方法,这种类称为JavaBean,一个类中除了set和get之外的方法,一般都是用public,除了写包时候。
多态(polymorphism)
多态指的是同一个方法的调用,由于对象不同可能会有不同的行为,现实生活中,同一个方法,具体实现会完全不同。
多态是指方法的多态
多态的要点:
1.多态是方法的多态,不是属性的多态(多态和属性无关)。
2.多态的存在要有三个必要条件:继承、方法重写、父类引用指向子类对象。
3.父类引用指向子类对象后,用该父类引用调用子类重写方法,此时多态就出现了。
package Test;
public class TestPoly {
public static void main(String[] args) {
Animal a = new Animal();
animalCry(a);//父类对象指向子类
Dog d = new Dog();
animalCry(d);
Cat c = new Cat();
animalCry(c);
}
static void animalCry(Animal a){ //传入不同对象执行方法不同
a.shout();
}
}
class Animal{
public void shout(){
System.out.println("叫了一声!");
}
}
class Dog extends Animal{
public void shout(){
System.out.println("旺旺");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵");
}
}
自动向下转型
Animal d = new Dog(); //自动向上转型:dog转成animal类,这里要注意类型是animal类,但是对象是Dog。
Dog d2 = (Dog)d; //强制向下转型:animal转为dog
//猫强转成dog,会产生编译错误
Animal c = new Cat();
Dog d3 = (Dog)c;
final修饰
final关键字作用:
1.修饰变量:被修饰得变量不可以改变,一旦赋值,就不能重新赋值。
final int MAX_SIZE = 120;
2.修饰方法:该方法不可被子类重写,但是可以被重载!
final void study(){}
3.修饰类:修饰的类不能被继承。比如:Math、String
final class A{}
数组(也为对象)
数组是相同类型数据的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称为一个元素,每个元素可以通过一个索引来访问。
特点
- 长度是确定的,数组一旦被创建,它的大小就是不可改变的
- 其元素必须是相同的,不允许出现混合类型
- 数组类型可以是任何数据类型,包括基本类型和引用类型
数组变量属于引用类型,数组也属于对象,数组中每个元素相当于该对象的成员变量。
声明方式
type[] arr_name;//推荐使用这种方式
注意事项
- 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。
- 声明一个数组的时候并没有数组真正被创建
- 构造一个数组,必须指定长度
package cn.yhq.array;
public class Test01 {
public static void main(String[] args) {
//int[] arr01:这是数组的声明,int[] arr01 = new int[10];这是数组的初始化
int[] arr01 = new int[10]; //索引下标[0 length-1]:
String[] arr02 = new String[5];
User[] arr03 = new User[3];
arr03[0] = new User(100,"yhq"); //不是将对象直接放到数组,是存放对象地址
arr03[1] = new User(101,"tony");
arr03[2] = new User(102,"jenny");
for (int i = 0;i<arr03.length;i++){
System.out.println(arr03[i].getAge());
}
}
}
class User{
private int id;
private String age;
public User(int id, String age) {
this.id = id;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
不是将元素对象直接放到数组,是存放对象地址
初始化方式
- 静态初始化
- 动态初始化
- 默认初始化
package cn.yhq.array;
public class Test02 {
public static void main(String[] args) {
//静态初始化
int[] a = {2,3,4};
User[] user01 = {
new User(100,"19"),
new User(101,"20")
};
//默认初始化
int[] b = new int[3];//默认给数组的元素进行赋值。赋值的规则和成员变量赋值的规则一样
//动态初始化
int[] a1 = new int[2];//动态初始化数组,先分配空间
a1[0] = 1;//给数组元素赋值
a1[1] = 2;
}
}
数组遍历
增强for循环for-each是专门用于读取数组或者集合中所有的元素,即对数组进行遍历。
//foreach循环:用于读取数组元素的值,不能修改元素的值
for (int m:num_array){
System.out.println(m);
}