目录
面向对象编程三大特征:
1. 封装性
2. 继承性
3. 多态性
1. 类与对象的定义及使用
1.1 类与对象的概念
类 就是指共性的概念,一类事物的总称。
对 象指一个具体的事物,类的一个实例。
1.2 类与对象的定义与使用
定义:
class 类名称 {
属性1;
属性2;
... ...;
方法1(){}
方法2(){}
... ...
}
例子:
class Person {
public String name;
public int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
}
class Person {
public String name;
public int age;
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person("张三",24);
}
}
产生对象:类名称 对象名称 = new 类名称 ();
注意此处:有new,就表明开辟了新空间
1.3 内存区域划分
大体上,可以将内存分为栈内存和堆内存。
1. 栈内存:存放局部变量(基本数据类型、对象的引用-->堆内存的引用等)
2. 堆内存:new出来的
举例:
Person person1 = new Person();
person1.name = "张三";
person1.age = 24;
1.4 引用传递
如果,代码变成这个样子呢?
Person person1 = new Person();
Person person2 = new Person();
person2 = person2;
引用传递其实就是一块堆内存可以被多个栈内存所指向。
2. 封装与构造方法
2.1 private 实现封装
上述代码中,类的属性和方法对外部可见,现实生活开发中,部分属性(比如说密码)及方法是不允许对用户可见的,因此引入了封装很有必要,体现了保护性。
将上述代码改写为:
属性或方法的可见不可见根据现实情况来决定。
此时,使用private 对类的属性进行了封装,如果要访问这个私有的属性,就必须提供以下方法:
Getter: 对属性内容的获得
Setter: 对属性内容进行设置、修改
例子:
class Person {
private String name;
private int 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 print(){
System.out.println("姓名:" + name + ": 年龄:" + age);
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person();
person1.setName("张三");
person1.setAge(24);
person1.print();
}
}
如果不添加setter 和 getter 方法,会发现代码报错,Text主类无法访问另外一个类中的属性。
因此我们可以得出,private 实现封装,只允许本类访问,外部类无法访问。
2.2 编写类的规则要求
1、编写类时,类中所有的属性都使用private 封装。
2、属性若要被外部访问,需定义setter 、 getter方法。
2.3 构造方法
产生对象:类名称 对象名称 = new 类名称 ();
所谓构造方法就是在使用new关键字实例化新对象的时候,由进行调用的方法。
需要遵循原则:
1. 方法名与类名称相同。
2. 构造方法没有返回值类型声明
3. 每个类中至少有一个构造方法,如果开发者没有明确定义构造方法, 系统会自动生成一个无参构造。
问题:构造方法没有返回值,为什么没有void 声明?
答:1、属性是在对象开辟堆内存时开辟的空间
2、构造方法是在创建对象(即new)时被调用的
3、普通方法是在空间开辟构造方法之后才可以多次调用的
编译器是根据程序的结构来区分普通方法和构造方法的,所以在构造方法之前没有返回值类型声明。
构造方法的调用和对象内存分配几乎是同时进行的,可以利用构造方法来为类中的属性进行初始化操作,从而避免多次setter调用。
2.4 构造方法与方法重载
成员方法不能调用构造方法!
成员方法有对象才能使用,而构造方法是产生对象时调用的方法。
2.5 匿名对象
//有名字的对象
Person person1 = new Person();
//匿名对象
new Person();
new Person().name;
有名字的对象可以多次使用,而匿名对象使用一次之后就会成为垃圾,由垃圾回收器在空闲的时候回收。 匿名对象在开发中较为少见。
3. this 关键字
1、this 调用本类属性
2、 this 调用本类方法
2.1、 this 调用类中的成员方法
2.2、 this 调用构造方法
3、 this 表示当前对象
3.1 this 调用本类属性
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void print(){
System.out.println("姓名:" + name + ": 年龄:" + age);
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person("张三",24);
person1.print();
}
}
输出:
姓名:张三: 年龄:24
从上述代码可以得出:当参数与类中属性同名时,类中的属性无法被正确赋值,此时需要加上this关键字,就可以给对象属性赋值。
注意:只要是在类中访问类中的属性,一定要加上this 关键字。
3.2 this 调用本类方法
3.2.1 this 调用普通方法
3.2.2 this 调用构造方法
class Person {
private String name;
private int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void fun() {
System.out.println("你好");
}
public void fun(String name) {
System.out.println("你好");
this.name = name;
}
public void fun(String name,int age) {
System.out.println("你好");
this.name = name;
this.age = age;
}
public void print(){
//调用成员方法
this.fun();
System.out.println("姓名:" + name + ": 年龄:" + age);
}
}
会发现上述代码中,多次重复写同样的语句,出现代码冗余,因此使用this调用构造方法。
注意:1. this(参数);要放在方法的首行
2. 构造方法的相互调用不能形成环。
3.3 this表示当前对象的引用
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void fun() {
System.out.println(this);
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
System.out.println(person1);
person1.fun();
System.out.println(person2);
person2.fun();
}
}
输出
Person@1b6d3586
Person@1b6d3586
Person@4554617c
Person@4554617c
当前的属性或者方法是通过哪个引用操作的,this 就表示该引用。
4. static 关键字(重要)
4.1 static 修饰变量(类属性)
class Person {
String name;
int age;
String Country = "中国";
public void print() {
System.out.println("姓名:" + this.name + "年龄:" + this.age);
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "张三";
person1.age = 24;
Person person2 = new Person();
person2.name = "李四";
person2.age = 18;
person1.print();
person2.print();
}
}
输出:
姓名:张三年龄:24
姓名:李四年龄:18
内存:
表示所有对象都具有的属性,在属性前面添加static 关键字即可。
在公共属性前添加static关键字之后,内存分配:
结论:
1. 访问static 属性,应使用 类名称 . 属性名
2. static与对象无关
4.2 static 方法(类方法)
class Person {
String name;
int age;
static String Country = "中国";
public static void setCountry(String c) {
Country = c;
}
public void print() {
System.out.println("姓名:" + this.name + "年龄:" + this.age + this.Country);
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person();
Person.setCountry("美国");
person1.print();
}
}
输出:
姓名:null年龄:0美国
注意:
static 方法不能调用非static定义的属性或者方法,但是非static 方法可以访问static 方法或者属性。
4.3 修饰代码块
(下文)
4.4 修饰内部类
(下文)
5. 代码块(重要)!!!
5.1 普通代码块
普通代码块:定义在方法,直接用 { } 括起来的代码就叫普通代码块。
一般因为方法中代码太长,为了避免变量重名会使用普通代码块。(使用较少)
5.2 构造块
构造块: 定义在类中,使用{ } 括起来的部分代码,叫做构造代码块。
构造块与构造方法的执行顺序:
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name,int age) {
System.out.println("构造方法");
this.name = name;
this.age = age;
}
{
System.out.println("构造块");
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person("张三",24);
}
}
输出
构造块
构造方法
先执行构造块,后执行构造方法。
有几个对象产生,构造块就执行几次。
5.3 静态块
静态代码块:定义在类中,使用static 定义的代码块。
5.3.1 非主类中的静态块
class Person {
private String name;
private int age;
public Person() {
}
static {
System.out.println("静态块");
}
public Person(String name,int age) {
System.out.println("构造方法");
this.name = name;
this.age = age;
}
{
System.out.println("构造块");
}
}
public class Text {
public static void main(String[] args) {
Person person1 = new Person("张三",24);
Person person2 = new Person("李四",18);
}
}
输出
静态块
构造块
构造方法
构造块
构造方法
优先于构造块执行,与对象无关。
无论产生多少实例化对象,静态块只执行一次。
5.3.2 主类中的代码块
public class Text {
static {
System.out.println("主类中的静态代码块");
}
public static void main(String[] args) {
new Text();
System.out.println("主方法");
}
输出:
主类中的静态代码块
主方法
主类中的静态块,优先于主方法(main)执行。
5.4 同步代码块
6. 内部类的定义及使用
内部类:在一个类的内部进行其他类结构的嵌套操作
6.1 内部类的概念
不含内部类:
class Outer {
private String msg = "你好";
public String getMsg() {
return this.msg;
}
public void fun() { //3. 调用Inner中的print方法
Inner in = new Inner(this);
in.print();
}
}
class Inner {
private Outer out;
public Inner(Outer out) {
this.out = out;
}
public void print() {
System.out.println(out.getMsg()); // 4. 执行此方法
}
}
public class Text {
public static void main(String[] args) {
Outer out = new Outer(); //外部类对象 1. 实例化Outer类对象
out.fun(); //外部类方法 2. 调用Outter类方法
}
}
内部类:
class Outer {
private String msg = "你好";
class Inner { //定义一个内部类
public void print() {
System.out.println(msg);
}
}
public void fun() { //外部类中的方法
Inner in = new Inner();//内部类对象
in.print();//内部类提供的方法
}
}
public class Text {
public static void main(String[] args) {
Outer out = new Outer(); //外部类对象
out.fun(); //外部类方法
}
}
上述代码实现了相同的功能,主方法代码没变,只是把内部类拆开到外部。 由此发现,引入内部类之后,程序变得有些混乱,但是方便操作外部类的访问。
6.2 内部类为什么存在
1. 内部类方法可以访问该类定义所在作用域中的数据,包括被private修饰的私有数据。
2. 内部类可以对同一包中的其他类隐藏起来。
3. 内部类可以实现java单继承的缺陷。
4. 需要回调函数可以使用匿名内部类来避免大量的代码。
6.3 内部类与外部类的关系
1. 对于非静态内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。
2. 内部类是一个相对独立的实体,与外部类不是继承的关系。
3. 内部类可以直接访问外部类的元素(包含私有域),但是外部类不能直接访问内部类的元素。内部类与外部类可以相互访问彼此的私有域(内部类访问外部类的私有域可以直接访问,外部类访问内部类的私有域必须通过内部类对象访问)
4. 外部类可以通过内部类引用间接访问内部类元素。
内部类可以访问外部类的私有域:
class Outter {
private String outName;
private int outAge;
class Inner {
private int InnerAge;
public Inner() {
Outter.this.outName = "张三";
Outter.this.outAge = 20;
}
public void print() {
System.out.println(outName);
System.out.println(outAge);
}
}
}
public class Text {
public static void main(String[] args) {
Outter.Inner inner = new Outter().new Inner();
inner.print();
}
}
外部类可以通过内部类引用间接访问内部类元素:
class Outter {
public void print() {
Inner inner = new Inner();
inner.print();
}
class Inner {
public void print() {
Inner inner = new Inner();
System.out.println("张三");
}
}
}
public class Text {
public static void main(String[] args) {
Outter.Inner inner = new Outter().new Inner();
out.print();
}
}
6.4 内部类的使用
6.4.1 内部类分类
1. 成员内部类
2. 静态内部类
3. 方法内部类
4. 匿名内部类
6.4.2 在外部类的外部创建内部类对象
6.4.3 在外部类的内部创建内部类对象
6.5 内部类详解
6.5.1 成员内部类
用法和成员方法差不多,只不过是个类
注意:
1. 成员内部类中不能存在任何static的变量和方法。
2. 成员内部类是依附于外部类的,所以只有先创建了外部类才能创建内部类。
3. private 不可以用在外部类的声明,但是可以用在内部类的成名(就像成员变量可以使private)
5.
成员内部类不能拥有静态域,但是可以访问外部类的静态
6.5.2 静态内部类
使用static修饰的内部类称为静态内部类。非静态内部类在编译完成之后会隐含一个引用,该引用指向它的外部类 ,而静态内部类没有这个引用,即就是说:
1. 静态内部列的创建不需要依赖于外部类,可以直接创建。
2. 静态内部类不可以使用任何外部类的非static成员变量及方法,而非静态内部类则都可以。
3. 成员内部类可以访问外部静态域,但不能拥有静态域
4. 静态内部类可以拥有成员域,但不能访问外部类的成员域
class Outter {
private static String msg = "你好";
static class Inner { // 定义一个内部类 ,只能使用外部类的static属性及方法
public void print() {
System.out.println("张三");
}
}
//在外部类中定义方法,实例内部类对象
public void fun() {
Inner inner = new Inner();
inner.print();
}
}
public class Text {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
inner.print();
}
}
6.5.3 方法内部类
方法内部类定义在外部类的方法中,只能在该方法中被使用,出了该方法就失效。
对这个类的主要使用就是应用与解决比较复杂的问题,但又不希望这个类是公共可用的,就产生了局部内部类。
1. 对外部完全隐藏,因此方法内部类不能有任何访问修饰符
代码实例:
class Outter {
private int num;
public void fun(int test) {
class Inner {
private void print() {
num++;
System.out.println(num);
System.out.println(test);
}
}
new Inner().print();
}
}
public class Text {
public static void main(String[] args) {
Outter out = new Outter();
out.fun(20);
}
}
输出:1 20
6.5.4 匿名内部类
匿名内部类就是一个没有名字的方法内部类,因此匿名内部类具有方法内部类所有的约束。
注意:
1. 匿名内部类是没有访问修饰符的。
2. 匿名内部类必须继承一个抽象类或者实现一个接口。
3. 匿名内部类中不能有static。
4. 匿名内部类是没有构造方法,因为没有类名。
5. 与局部内部类相同,匿名内部类也可以引用方法形参,该形参声明为final。
6. 使用一次就销毁了
代码实例:
interface MyInterface {
void test();
}
class Outter {
private int num;
public void fun(int test) {
new MyInterface() {
@Override
public void test() {
System.out.println("匿名内部类" + test);
}
}.test();
}
}
public class Text {
public static void main(String[] args) {
Outter out = new Outter();
out.fun(20);
}
}
输出:匿名内部类 20
7. final 关键字
1. 使用final修饰类、方法、属性。
2. final成员变量必须在声明的时候初始化或者在构造器中初始化,否则会报错。
3. 使用final定义的类不能有子类,
4. final一旦修饰一个类之后,该类的所有方法默认都会加上final 修饰。
5. 使用final 定义的方法不能被子类覆写。
6. 使用final 定义的变量就成为了常量,必须在声明的时候赋值,并且不能被修改。
7. 使用final修饰的变量不能再次赋值。
8. 定义常量,常量全部都用大写,多个单词知寄件用‘ _ ’分割。
public static final int MAX_COUNT = 20;