文章目录
- 面向对象基础
- 方法
- 构造方法
- 默认构造方法
- 多构造方法
- 方法重载
- 继承
- 多态
- 抽象类
- 接口
- 静态字段和静态方法
- 包
- 作用域
- 内部类
- 写在最后
https://www.liaoxuefeng.com/wiki/1252599548343744/1255943520012800
面向对象编程Object-Oriented Programming,简称OOP,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法
面向对象基本概念
类
实例
方法
面向对象的实现方式
继承
多态
Java语言提供的机制
package
classpath
jar
Java标准库提供的核心类
字符串
包装类型
JavaBean
枚举
常用工具类
面向对象基础
面向对象编程,就是通过对象的方式,把现实世界映射到计算机模型的一种编程方法
class和instance
class是一种对象模板,定义了如何创建实例
instance是对象实例,是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自的属性可能不同
class Person{
// field
public String name;
public int age;
}
Person ming=new Person();
ming.name="Xiao Ming";
ming.age=18;
Person hong=new Person();
hong.name="Xiao Hong";
hong.age=19;
方法
class中直接把field用public暴露给外部可能会破坏封装性
直接操作filed,容易造成逻辑混乱,未来避免外部代码直接访问field,可以用private修饰field,拒绝外部访问
class Person{
// field
private String name;
private int age;
}
修改之后,外部无法访问field,想要给field赋值需要使用方法(method)来让外部代码间接修改field:
class Person{
// field
private String name;
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
if (age < 0 || age > 100) {
throw new IllegalArgumentException("invalid age value");
}
this.age = age;
}
}
一个类通过定义方法,就可以给外部代码暴露一些操作的接口,同时,内部自己保证逻辑一致性。
private方法
private方法不允许外部调用,但内部方法可以调用
this变量
方法内部,可以使用一个隐含的变量this,始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。
方法参数
方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。
public void setNameAndAge(String name, int age) {
...
}
可变参数
可变参数用类型…定义,可变参数相当于数组类型
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String
参数绑定
调用方把参数传递给实例方时,调用时传递的值会按照参数位置一一绑定
// 基本类型
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // n的值为15
p.setAge(n); // 传入n的值
System.out.println(p.getAge()); // 15
n = 20; // n的值改为20
System.out.println(p.getAge()); // 15还是20? ---> 15
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
修改外部的局部变量n,不影响实例p的age字段,原因是setAge()方法获得的参数,复制了n的值,因此,p.age和局部变量n互不影响。
// 引用类型
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname); // 传入fullname数组
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
System.out.println(p.getName()); // "Homer Simpson"还是"Bart Simpson"?---->"Bart Simpson"
}
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象)。
// 引用类型
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // 传入bob变量
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bob改名为Alice
System.out.println(p.getName()); // "Bob"还是"Alice"?---->"Bob"
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
解释
构造方法
在创建对象实例时就把内部字段初始化为合适的值?需要构造方法。
在创建Person实例的时候,一次性传入name和age
public class Main {
public static void main(String[] args) {
Person p = new Person("Xiao Ming", 15);
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
和普通方法相比,构造方法没有返回值,也没有void,调用构造方法,必须用new操作符
默认构造方法
任何class都要构造方法
如果一个类没有定义构造方法,编译器会自动为我们生成一个默认构造方法,它没有参数,也没有执行语句
class Person {
public Person() {
}
}
如果自定义了一个构造方法,编译器就不再自动创建默认构造方法
如果既要能使用带参数的构造方法,又要保留不带参数的构造方法,要把两个方法都定义出来
多构造方法
可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
this.age = 12;
}
public Person() {
}
}
一个构造方法可以调用其他构造方法,这样便于代码复用,调用其他构造方法的语法是this(…)
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this(name, 18); // 调用另一个构造方法Person(String, int)
}
public Person() {
this("Unnamed"); // 调用另一个构造方法Person(String)
}
}
方法重载
在一个类中,可以定义多个方法。如果有一系列方法,功能都是类似的,只有参数有所不同,那么,可以把一组方法名做成同名方法
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
这种方法名相同,但各自的参数不同,称为方法重载(Overload)
方法重载的返回值类型通常都是相同的
继承
继承是面向对象编程中非常强大的一种机制,可以复用代码,Java使用extends关键字来实现继承
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student extends Person {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
}
Person称为超类super class,父类parent class,基类base class,把student称为子类subclass,扩展类extended class
继承树
在Java中,没有明确写extends的类,编译器会自动加上extends Object。所以,任何类,除了Object,都会继承自某个类。
Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类。
protected
继承中,子类无法访问父类的private字段或者private方法,这使得继承的作用被削弱。
为了让子类可以访问父类的字段,需要把private改为protected,用protected修饰的字段可以被子类访问
protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问。
super
super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。
class Student extends Person {
public String hello() {
return "Hello, " + super.name;
}
}
阻止继承
正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。
从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。
public sealed class Shape permits Rect, Circle, Triangle {
...
}
多态
class Person {
public void run() {
System.out.println("Person.run");
}
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
抽象类
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法
定义抽象方法,类也要定义为抽象类
abstarct class Person{
public abstarct void run();
}
抽象类无法实例化,只能用于被继承,可以强迫子类实现其定义的抽象方法,否则编译会报错。抽象方法实际上相当于定义了“规范”。
接口
在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface。
interface Person {
void run();
String getName();
}
interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。
当一个具体的class去实现一个interface时,需要使用implements关键字。
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}
java中,一个类只能继承自另一个类,不能从多个类继承,但是,一个类可以实现多个interface
class Student implements Person, Hello { // 实现了两个interface
...
}
接口继承
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
静态字段和静态方法
在一个class中定义的字段,称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。
还有一种字段,是用static修饰的字段,称为静态字段:static field。
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。
class Person {
public String name;
public int age;
// 定义静态字段number:
public static int number;
}
包
Java中使用包package来解决名字冲突
一个类总属于某个包,类名只是一个简写,真正完整的类名是 包名.类名
包作用域
位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。
作用域
public
定义为public的class、interface可以被其他任何类访问
private
定义为private的field、method无法被其他类访问
一个类内部的嵌套类拥有访问private的权限
public class Main {
public static void main(String[] args) {
Inner i = new Inner();
i.hi();
}
// private方法:
private static void hello() {
System.out.println("private hello!");
}
// 静态内部类:
static class Inner {
public void hi() {
Main.hello();
}
}
}
protected
protected作用于继承关系。定义为protected的字段可以被子类访问,以及子类的子类
package
包作用域是指一个类允许访问同一个package的没有public、private修饰的class,以及没有public、protected、private修饰的字段和方法
final
用final修饰class可以阻止被继承;
final修饰的method可以阻止被子类覆写;
final修饰field可以阻止被重新赋值;
final修饰局部变量可以阻止被重新赋值;
内部类
1、Inner class
class Outer {
class Inner {
// 定义了一个Inner Class
}
}
Outer.Inner inner = outer.new Inner();
2、Anonymous class
不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested");
outer.asyncHello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
void asyncHello() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}
3、Static Nested Class静态内部类
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer {
private static String NAME = "OUTER";
private String name;
Outer(String name) {
this.name = name;
}
static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}
用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。
Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
Static Nested Class是独立类,但拥有Outer Class的private访问权限。
写在最后
这些抄一遍好像也记不住,,,,还是要定期回顾。。。。