面向对象
关于面向对象的知识点理解
面向对象:
是设计者思维,是在创造某个产品时的,从宏观角度把握、整体上分析整个系统。
解决复杂、需要协作的问题时使用面向对象。也就是怎么设计这个事物?比如:造车就是面向对象的思考。(整体设计)
面向过程考虑谁来做?以类/方法为最小单位
面向过程:
是执行者思维,解决简单问题时使用面向过程。比如:如何开车?就是面向过程的思考。(执行和处理数据)
面向过程考虑怎么做?以函数为最小单位
二者相辅相成不是独立的。
属性是独享的,方法是共享的。
方法的重载
关于类、方法、构造器
属性
类可以看作是一个模版,或图纸,系统根据类的定义来造出对象。
类叫:class。对象:我们叫做Object(目标),instance(实例)。
属性 是用来定义该类或该类对象的数据或者静态特征。
方法 用于定义该类或该类的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。
方法其实就是在该类下面,所有属性共有的特征。
构造器的4个要点:
1.构造器通过 new 关键字调用!
2.构造器虽然有返回值,但 不能 定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值
3.如果我们没有定义构造器,则编译器会 自动定义 一个无参数的构造方法。如果已定义则编译器 不会 自动添加!
4.构造器的方法名 必须 和类名一直(class 后面的类名)
构造器的作用:不是创建对象,因为在创建之前这个对象已经创建好了。并且对象有默认的初始值
调用构造器的目的只是给属性进行赋值的操作。
⚠️注意:我们一般不会在构造器中进行初始化操作,因为那样每个属性的值是一样的。
在重载构造器之后,系统不会在给你分空构造器,这时候调用则会出错
与顺序没有关系,可以随便写
成员变量、局部变量和静态变量的区别
在(成员变量)栈、在(局部变量)堆、在(静态变量)方法区里。
(1)在类中的位置不同
成员变量:类中方法外
局部变量:方法定义中或者方法声明上(代码块中)
(2)在内存中的位置不同
成员变量:在堆中
局部变量:在栈中
(3)生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
(4)初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值,然后才能使用。需要初始化值
this的用法
this的本质就是创建好的对象的地址.由于在构造方法调用前,已经创建。因此,在构造方法中也可以使用this代表当前对象。
构造器不是用来建对象的,只是用来初始化对象的属性
加深理解this:
创建对象的4步:
1.分配对象的空间,并对对象成员初始化为0或空
2.执行属性值的显式初始化(比如int id;哪默认为0,int id =3,则为3)
3.执行构造方法
4.返回对象的地址给相关的变量
this 最常用的用法:
1.this是用于指明当前对象,最常用的形式(在构造方法中,指向初始化的对象)如下
public User(int id, String name) {
System.out.println ("正在初始化已经创建好的对象" + this);//this指代的就是这个刚被创建出来的对象
this.id = id;//不写this无法区分成员变量id(在堆中,在类中方法外,有默认值)和局部变量id(方法定义或方法声明中,在栈中,必须定义,赋值才可以使用)
this.name = name;
}
public void login(){
System.out.println (this.name+"需要登录!");//不写this效果一样
}
public User(int id,String name,String pwd){
this(id,name);//this(),可以用于调用其他构造器,但必须位于第一行
}
2.this可以用于重载的构造方法中,但必须位于第一行
3.this 不能用于static方法中(this是必须从属对象,main中没有对象,属于类)
static用法
public class Demo {
int id;
static int sid;
public void a() {
System.out.println ("---------a");
System.out.println (sid);
System.out.println (id);
}
//1.static和public 是修饰符,并列的没有先后顺序,先写谁都可以
static public void b() { //sid 是先于对象加载的
// a();//3.在静态方法中不能访问非静态方法
System.out.println ("---------b");
System.out.println (sid);
// System.out.println (id);//2.在静态方法中不能访问非静态的属性
// System.out.println (this.id);//4.在静态方法中不能使用this关键字
}
public static void main(String[] args) {
Demo d = new Demo();
// Demo.a();//5.非静态的方法可以用对象名.方法名去调用,不能用类.方法名
d.a ();
Demo.b ();//6'静态的方法可以用对象名.方法;类名.方法名(推荐)去调用
d.b ();
b();//在同一个类中可以直接调用
}
}
static 声明的成员变量为静态成员变量/类变量,生命周期和类相同,声明的方法叫静态方法,static修饰的都是位于类里面不是对象里面
虚拟机内存内存简单三个分区域:
stack栈,heap堆、方法区 method area
构造方法则在栈中进行,而方法代码则在方法区里
虚拟机栈(stack)特点:
1.栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈(存储局部变量、操作数、方法出口等)
2.jvm为每个线程创建一个栈,用于存放线程执行的方法信息(实际参数、局部变量等)
3.栈属于线程私有,不能实现线程共享
4.栈存储,先进后出,后进先出
5.栈是系统分配的速度快,是连续内存空间,构造方法需要开辟一个栈帧
堆(heap)特点:
1.堆用于存储创建好的对象和数组(数组也是对象)
2.jvm只有一个堆,被所有线程共享
3.堆是不连续的内存空间,分配灵活,速度慢
方法区(静态区,也是堆)特点:
1.只用于存储类和常量相关信息
2.存放程序永远不变或者唯一的内容。(类信息【class对象、静态变量、字符串常量)
内存分析
总结:
1.进行创建对象之时,会先在方法区加载Person 类字节码信息,以及在堆中创建默认对象,并默认做了初始化,有地址
2.在之后会调用new关键字,调用构造器,构造器是一个方法会开辟一个栈帧,先把值赋给形参,再把值赋给局部变量,完成赋值。方法的栈消失(形惨、局部变量)在栈中消失。(字符串会在方法区的常量池里)
3.对象已经创建好,将值赋给P。
(1)简单的代码分析
(2)
(3)
结果分析:
包机制(package import)
package
包名:域名倒着写即可,再加上模块名,便于内部管理类。如:com.sur.test
解决:类的同名和类的管理
com.gao和com.gao.car,这两个包没有包含关系,但两个完全独立的
jak中的主要包
java.lang 包含Java语言的核心类。如:String、Math、System等等(可以直接用)
java.awt
java.net
java.io
java.util
**
导入类import
若要是用其他包的类,需要使用import 导入,从而通过在本类中直接通过类名调用
注意:java 默认导入java.lang包下的所有类,可直接使用;
若导入的两个同名类,只能用包名+类名来显示调用相关类。如:
java.util Date d = new java.util.Date();
静态导入:
静态导入(static import)作用是用于导入指定类的静态属性和静态方法,这样我们可以直接使用静态属性和静态方法。
import static java.lang.Math.*;//导入Math类的所有静态属性
import static java.lang.Math.PI;//导入Math类的PI属性
继承
继承的作用:
1.代码的复用,更易实现类的拓展;2.方便对事物的建模(对于相同的内容的复用)
extends (拓展)
继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
**
使用要点:
1.Java是单继承(一个子类只能有一个父类),没有多继承
2.java中类没有多继承,接口有多继承。
3.子类继承父类,得到父类的全部属性和方法(除了父类构造方法)
4.若定义一个类时,没有调用extends,则父类是:java.lang.Object
public class Person/*extends object*/ {//父类
String name;
int height;
public void rest() {
System.out.println("休息");
}
public static void main(String[] args) {
Student w = new Student("zhangsan",180,60);
System.out.println(w instanceof Person);
}//instanceof 的使用,
}
class Student extends Person {//子类,继承了父类的属性和方法
// int id;
// String name;
int score;
// public void rest() {
// System.out.println("休息");
// }
public void study() {
System.out.println("学习" + this.name);
}
Student(String name, int height, int score) {
this.name = name;
this.height = height;
this.score = score;
}
}
instanceof 运算符
instanceof是二元运算符,左边是对象,右边是类。当对象是右面类或子类创建的对象时,返回true,否则false。如上代码。
**
方法的重写(override):
子类通过重写父类的方法
方法重写的三个要点:
1."==" 方法名、形参列表相同
2."<=" 返回值类型和声明异常类型,子类小于等于父类(如:父类是person,子类不能是object)
3.">=" 访问权限(如:public),子类大于等于父类
package Test;
import org.w3c.dom.ls.LSOutput;
public class TestOverride {//重写是子类通过重写父类的方法。
public static void main(String[] args) {
Vehicle v1 = new Vehicle();
Vehicle v2 = new Vehicle();
Vehicle v3 = new Vehicle();
v1.run();
v2.run();
v3.run();
v2.stop();
v3.stop();
}
}
class Vehicle {//交通工具类
public void run() {
System.out.println("跑。。。");
}
public void stop() {
System.out.println("停止不动");
}
}
class Horse extends Vehicle{//马也是交通工具
public void run(){ //重写父类方法
System.out.println("四蹄翻飞");
}
}
class Plane extends Vehicle{
public void run(){//重新定义父类方法
System.out.println("天上飞");
}
public void stop(){
System.out.println("空中不能听");
}
}
final关键字
final关键字作用:
修饰变量:被他修饰的变量不可改变,一但赋了初始值,就不能重新赋值。
修饰方法:该方法不可被子类重写。但可以被重载。
修饰类:修饰类不能被继承
继承和组合
is a用继承,如:Student is a person;has a 用组合,如:笔记本和芯片的关系
“组合”的核心是“将父类对象作为子类的属性”,然后,“子类通过调用这个属性来获得父类的属性和方法。
package Test;
public class Test {
public static void main(String[] args) {
Student s =new Student("高淇",172,"Java");
s.person.rest();//s.rest()
}
}
class Person{
String name;
int height;
public void rest(){
System.out.println("休息一会");
}
}
class Student /*extends Person*/{
Person person = new Person();
String major;
public Student(String name,int height,String major){
//天然拥有父类的属性
this.person.name=name;// this.name = name;
this.person.height=height;//this.height = height;
this.person.rest();
this.major=major;
}
}
Object类
Object是所有Java类的根基类,在类声明中未使用extends关键字指明的父类,则默认继承Object类。
除了构造方法之外的所有的属性和方法都被继承。但是不是所有的都能使用。
结构试图快捷键 command+7 看源码 command+左键
常见字符串:
toString 方法
Object 类中定义public String to String()方法,返回值是String类型。
public String toString(){
return
}
==和equals方法
"=="代表双方是否相同,如果是基本类型则表示相等的值,如果是引用类型则表示地址相等则为同一个对象,否则表示不同。
自动生成构造器、get、set方法、equal等。快捷键:control+return
super 关键字
super 含义可以看作是 直接父类对象的引用
封装(encapsulation)
封装是面向对象三大特征之一,对于程序合理的封装让外部调用更方便,更利于协作,同时,对于实现者来说也更容易修正和改版代码。
作用和含义
程序设计追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。
优点
- 提高代码安全性
- 提高代码复用性
- “高内聚”:封装细节,便于修改内部代码,提高维护性
- “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作
封装—使用访问控制符
关于Protected的两个细节
若父类和子类在同一个包中,子类可以访问父类的Protected成员,也可以访问父类对象的Protected成员
若子类和父类不在同一个包中,子类可访问父类的Protected成员,不能访问父类对象的Protected成员
封装的使用细节
简单规则:
- 属性一般使用private访问权限
- 属性私有后,提供相应get/set方法来访问相关属性,通常用public修饰,以提供属性的赋值与读取操作(boolean变量get方法是is开头)
- 方法:一些只用于本地的类的辅助性方法可以使用private修饰,希望其他类调用的方法用public
注意:
引入get和set方法, set是设置,而get是获取,这两个方法是对数据进行设置和获取用的。而且,在类中使用set和get方法时,都是在set和get后面跟上一些特定的词来形成特定意思的方法名,比如set xxx()和get xxx(),表示设置xxx和获取xxx。(快捷键 control+return)
多态
概念:多态指的是同一方法调用,由于对象不同可能会有不同的行为。
多态的要点
- 多态是方法的多态,不是属性的多态(多态与属性无关)
- 多态存在的3个必要条件:继承,方法重写,父类引用指向子类对象
- 父类引用指向子类对象后,用该父类调用子类重写的方法,多态就出现了
package com.bjsxt.polymophism;
public class Animal {
public void shout(){
System.out.println("叫了一声");
}
}
//独立的类
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
class Tiger extends Animal{
@Override
public void shout() {
System.out.println("wawaa");
}
}
package com.bjsxt.polymophism;
public class Test {
static void animalCry(Animal a) {
a.shout();//多态!!!
}
public static void main(String[] args) {
//创建对象
Dog d = new Dog();
animalCry(d);//animal a =d ;父类引用指向子类对象
animalCry(new Cat());//animal a =new Cat();父类引用指向子类对象
animalCry(new Tiger());
}
}
对象的转型
父类引用指向子类对象,我们称为向上转型,属于自动转换。
向上转型后的父类引用变量,只能调用它编译类型的方法,不能调用它运行时的方法。这时,我们就需要进行类型的强制转换,称之为向下转型!
//测试类型的转换
Animal a = new Dog();//向上类型转换,自动的
Animal b = new Cat();
a.shout();
// a.seeDoor();需要强转
Dog d2 =(Dog) a;//强制类型转化,向下类型转换,父类转子类
d2.seeDoor();//只能调用它编译类型的方法,不能调用它运行时类型的方法,这时需要强转,称为向下转型!
Cat c4 =(Cat) b;//
c4.catchMouse();
Cat c3 =(Cat) a;//java.lang.ClassCastException(可以编译,不可以执行,类型转换错误)
c3.catchMouse();
}
}
抽象方法—抽象类
抽象方法
使用abstract修饰方法,没有方法体,只有声明。定义的是一种规范。就告知子类必须给抽象方法提供具体实现
抽象类
包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们可以做到严格限制子类的设计,使得子类之间更加通用
package com.bjsxt.abstractClass;
public abstract class Student {
private String name;//普通的属性和方法不受影响
abstract public void study();//定义完抽象方法,类也变成抽象类
abstract public void exam();//抽象类方法必须由子类来实现
//创建方法,跟抽象类无关
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//构造方法
public Student(String name) {
this.name = name;
}
//无参构造器 (只要某个类是父类,则就加一个无参构造器)
Student(){
}
}
package com.bjsxt.abstractClass;
public class Test {
public static void main(String[] args) {
//Student s =new Student("ddd"); 抽象类不能创建对象
Student s = new SxStu();//父类引用指向子类对象
s.study();
s.exam();
}
}
class SxStu extends Student{
@Override
public void study() {
System.out.println("好好学习!!!");
}
@Override
public void exam() {
System.out.println("考100分!!");
}
}
使用要点
- 抽象方法的类只能定义抽象类
- 抽象不能实例化,(抽象类)不能用new来创建对象
- 抽象类可包含 属性、方法、构造方法。但构造方法不能用new实例,只能被子类调用
- 抽象类只能用来继承
- 抽象方法必须被子类来实现
接口(interface)的实现
接口本质就是规范,定义的一组规则,是面向对象的精髓
三个类的区别
- [ ] 普通类:具体实现
- [ ] 抽象类:具体实现,规范(抽象方法)
- [ ] 接口:规范(jdk8 以后可加特殊的方法)
接口的详细说明
- [ ] 访问修饰符:只能是public或者默认
- [ ] 接口名:和类名采用相同的命名机制
- [ ] extends:接口可以多继承
- [ ] 常量:接口中的属性只能是常量,总是public static final,不写也可以
- [ ] 方法:接口中的方法只能是public abstract,省略的话,也是public abstrct
要点
- [ ] 子类通过implements来实现接口中的规范
- [ ] 接口不能创建实例,但可用于声明引用对象变量类型
- [ ] 一个类实现接口,必须实现接口中的所有方法,并且这些方法只能是public
- [ ] jdk1.8(不含8)之前,接口中只能包含静态常量、抽象方法、不能是普通属性、构造方法和普通方法
- [ ] jak1.8(含8)之后,接口中包含普通静态方法和默认方法(拓展方法)
package com.bjsxt.testInterface;
/**
* 这时一个飞行器的接口
*/
public interface Volant {
/**
* 表示飞行器在地球上的最高高度,单位是:公里
*/
/*public static final */ int MAX_HIGHT = 100;
/**
* 飞行器方法,飞行器可以起飞
*/
/* public abstract */void fly();
/**
* 可以让飞行器停止,若在空中悬停,如果在地上则停止
*/
void stop();
}
/**
* 善良接口
*/
interface Honest {
void helpOther();
}
package com.bjsxt.testInterface;
public class SuperMan implements Volant,Honest{//接口中的所有方法必须实现,用英文逗号隔开
@Override
public void fly() {
System.out.println("横着飞");
}
@Override
public void stop() {
System.out.println("竖着停");
}
@Override
public void helpOther() {
System.out.println("哪里call,飞哪里");
}
public static void main(String[] args) {
SuperMan m1 =new SuperMan();//或者写成Volant m1 =new SuperMan
m1.fly();//无需修改,fly()属于Volant 类
m1.helpOther();//这里则需要强转 Honest h=(Honest) m1;
}
}
默认方法
默认方法和抽象方法的区别是抽象方法必须要实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所以这个接口的实现类都会通过继承得到这个方法
interface A{
default void moren(){//必须加default
System.out.println("我是接口A中的默认方法!");
}
}
class Test_A implements A{
@Override
public void moren() {
System.out.println("Test_A");
}
}
public class Test {
public static void main(String[] args) {
A a =new Test_A();
a.moren();
}
}
静态方法
如果子类中定义相同的静态方法,那就是我完全不同的方法了,直接从属于子类,可以通过子类名直接调用
public class Testjing {
public static void main(String[] args) {
B.staticMethod();
Test_B.staticMethod();
}
}
interface B{//接口属于特殊的类
public static void staticMethod(){
System.out.println("A.staticMethod");
}
}
class Test_B implements B{//创建子类
public static void staticMethod(){
System.out.println("Test_B.staticMethod");
}
}
接口多继承
interface B{
void test0b();
}
interface C{
void test0c();
}
/**接口可以多继承,接口D继承B和C*/
interface D extends B,C{
void test0d();
}
public class Test0 implements D{
@Override
public void test0b() {
}
@Override
public void test0c() {
}
@Override
public void test0d() {
}
}
String 类和常量池
- 全局字符串常量池(String Pool)
- class文件常量池(Class Constant Pool)
- 运行时常量池(Runtime Constant Pool)
字符串相等判断(以后一般判断字符串值是否相等,使用equals())
public class Test1 {
public static void main(String[] args) {
String str1 =new String("abcdef");
String str2 ="abcdef";
System.out.println(str1==str2);
System.out.println(str2.equals(str1));//比较我们两个字符串对象是不是一样的
}
}
String类常用方法
char charArt(index) // 返回字符串中第index个字符
boolean equals(String other) //字符串与other相等,返回true,否则返回false
boolean equalsIgnoreCase(String other)//字符串与other(忽略大小写)返回true,否则返回false
int indexOf(String str) //返回从头开始查找str在字符串索引中的位置,未找到str,返回-1
lastIndexOf(String str) //返回从尾查找str在字符串索引中的位置,未找到,返回-1
int length()// 返回字符串的长度
String replace(char oldChar,char newChar)//返回新的字符串,替换
boolean starWith(String prefix) //判断字符串以prefix 开始,返回true,否则返回false
boolean endWith(String prefix) //判断以prefix结尾,返回true,否则返回false
String subsring(int beginIndex)//返回一个新的字符串,从beginIndex到串尾
String substring(int beginIdex,int endIdex) //返回一个新的字符串,从beginIndex到endIndex-1
String toLowerCase() //返回一个新字符串,原始字符串所有大写改成小写
String toUpperCase() //返回一个新字符串,原始字符串所有小写改成大写
String trin()//返回一个新的字符串,去掉首尾空格
//举例
public class test03 {
public static void main(String[] args) {
System.out.println("abcdef".charAt(5));//f
System.out.println("abcdef".equals("abcdef"));//true
System.out.println("ab".equalsIgnoreCase("AB"));//true
System.out.println("abcde".indexOf("cd")); //第2位
System.out.println("abcde".lastIndexOf("d"));//第3位
System.out.println("abcde".length());//5
System.out.println("abcde".replace("bc","BC"));//aBCde
System.out.println("abcde".startsWith("bc"));//false
System.out.println("abcde".endsWith("de"));//true
System.out.println("abcde".substring(1));//bcde
System.out.println("abcde".substring(2,4));//cd
System.out.println("abcde".toUpperCase());//ABCDE
System.out.println("aBCDe".toLowerCase());//abcde
System.out.println(" a b ".trim());//a b
}
}
内部类
内部类是把一个类放在另一个类的内部定义。
内部类只是一个编译时概念,一旦编译成功,就会成为完全不同的两个类。
内部类的作用:
内部类提供更好的封装,只能让外部类直接访问,不允许同一个包中的其他类直接访问。
内部类可以直接访问外部类的私有属性,内部类被当成其他外部类的成员,但外部类不能访问内部类
//非静态内部类
//外部类
public class Outer1 {
private int age = 10;
private void show() {
System.out.println("要你好看");
}
//内部类
public class Inner1 {
private String name = "tom";
private int age = 20;
public void showInner() {
System.out.println("Inner.showInner");
System.out.println(age);
System.out.println(Outer1.this.age);//当外部类属性和内部类属性发生重名时,可以通过:Outer.this.成员名
show();//内部类可以直接使用外部类的成员
}
}
public static void main(String[] args) {
Outer1.Inner1 inn01 = new Outer1().new Inner1();
inn01.showInner();
//另一种写法
Outer1 out2 = new Outer1();
Inner1 inn02 = out2.new Inner1();
inn02.showInner();
}
}
//静态内部类
class Outer2 {
private int a =10;
private static int b =20;
//相当于外部类的一个静态成员
static class Inner{
public void test(){
// System.out.println(a); //静态内部类不能访问外部非静态普通属性
System.out.println(b);//静态内部类可以访问外部类静态属性
}
}
}
public class jingtai {
public static void main(String[] args) {
//通过new外部类名.内部类名()来创建内部对象
Outer2.Inner inner =new Outer2.Inner();
inner.test();
}
}
//匿名内部类
//匿名内部类测试
public class TestAnonymousInnerclass {
public void test(A a){
a.run();
}
public static void main(String[] args) {
TestAnonymousInnerclass tai =new TestAnonymousInnerclass();
tai.test(new AA());
tai.test(new A() {
@Override
public void run() {
System.out.println("TestAnonymousInnerclass.run");
}
});
}
}
//有名字,可以反复使用
class AA implements A{
@Override
public void run() {
System.out.println("AA.run");
}
}
interface A{
void run();
}