面向对象编程是一种对现实世界建立计算机模型的一种编程方法。简称OOP。OOP:Object Oriented Programming
在现实世界中,当我们提到动物这个概念,实际上它是一个抽象的概念。而具体动物是指老虎,狮子,大象等等。在对应的计算机模型中,我们把动物这种抽象的概念称之为class,也就是类。而那些具体的对象称之为实例,并且用不同变量标识不同的实例。
class是对象的模板。
Instance是对象实例。
一个class可以包含多个field(字段)
field用来描述一个class的特征
class实现了数据封装
new操作符可以创建一个实例
定义一个引用类型变量来指向实例
通过变量来操作实例
通过变量.字段来访问实例字段
package com.day.one; public class Main { public static void main(String[] args) { Person ps = new Person(); ps.name = "小明"; ps.age = 12; System.out.println(ps.name); System.out.println(ps.age); } }
一个class可以包含多个filed
直接把filed用public暴露个外部可能破环了封装
用private修饰field可以拒绝外部访问
定义public方法可以间接修改filed
public方法封装了数据访问
通过方法访问了实例字段更安全
通过变量.方法名()来调用实例方法
修饰符
方法返回值
方法名字
方法参数列表
方法的返回值通过return语句实现
没有返回值可以剩return
方法可以使用隐式变量this
this指向当前实例
this.filed 可以访问当前实例的字段
在不引起歧义情况下,可以剩略this
局部变量名优先
实例变量.方法名(参数)
可以忽略方法返回值
用于接收传递给方法的变量值
分为 基本类型参数 引用类型参数
外部代码不可访问private方法
内部代码可以调用自己的private方法
package com.day.one; public class Main { public static void main(String[] args) { Person ps = new Person(); ps.setName("小明"); ps.setAge(12); System.out.println(ps.getName()); System.out.println(ps.getAge()); System.out.println(ps.getBrith()); } }
package com.day.one; public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name.trim(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getBrith(){ return this.calcBrith(2018); } private int calcBrith(int year) { return year - this.age; } }
构造方法可以在创建对象实例时初始化对象实例
构造方法名就是类名
构造方法的参数没有限制
构造方法没有返回值(也没有void)
必须使用new操作符调用构造方法
如果一个类没有定义构造方法,编译器会自动生成一个默认的构造方法:无参数,无执行语句
如果自定义了构造方法,编译器就不再自动创建默认的构造方法
初始化顺序
可以定义多个构造方法,编译器通过构造方法的参数数量,位置和类型区分
一个构造方法可以调用其他的构造方法,便于代码复用,使用this()调用
package com.day.one; public class Main { public static void main(String[] args) { Person ps = new Person(); Person ps1 = new Person("liming", 11); System.out.println(ps.getName()); System.out.println(ps.getAge()); System.out.println(ps1.getName()); } }
package com.day.one; public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { this("libai", 14); } public String getName() { return name; } public void setName(String name) { this.name = name.trim(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getBrith() { return this.calcBrith(2018); } private int calcBrith(int year) { return year - this.age; } }
多个方法的方法名相同
但各自的参数不同:
方法返回值类型通常是相同
方法重载的目地:
l 相同功能的方法使用同一名字
l 便于调用
package com.day.one; public class Main { public static void main(String[] args) { Person ps = new Person(); ps.setName("一个参数"); System.out.println(ps.getName()); ps.setName("第二", "参数"); System.out.println(ps.getName()); } }
package com.day.one; public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { this("libai", 14); } public String getName() { return name; } public void setName(String name) { this.name = name.trim(); } public void setName(String firstName, String lastName) { this.name = firstName + " | " + lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getBrith() { return this.calcBrith(2018); } private int calcBrith(int year) { return year - this.age; } }
继承是代码复用的一种方式。
超类(super),父类,基类
子类(subclass),扩展类
Java默认所有类继承Object
Java只允许一个类继承自一个类
一个类有且仅有一个父类(Object除外)
父类定义的私有字段不允许被子类访问
父类定义的所保护字段允许被子类访
Protected把字段和方法的访问权限控制继承树内部
supser关键字表示父类(超类)
构造方法的第一行语句必须调用super()
没有super()时编译器会自动生成super()
如果父类没有定义默认的构造方法,有其他带参数的构造方法,编译器替子类生成的super()就调用不了,所以子类就必须显示调用super(),传入需要的参数。
当我们定义一个类的时候,实际上就是定义一种数据类型。当我们定义了继承的时候,实际上就是在这些数据类型之间加上继承的关系,当我们创建一个实例对象的时候,我们需要把它赋值一个引用类型变量,让这个变量指向这个实例对象。但我们遇到一个问题:
可以对实例变量进行向上转型(upcasting)
向上转型把一个子类型安全地变为更加抽象的类型
可以对实例变量进行向下转型(downcasting)
向下转型把抽象的类型变成一个具体的子类型
向下转型很可能会报错:ClassCastException
Instanceof操作符可以判断对象的类型
Student 不宜从Book继承
Student可以持有一个Book实例
继承是is关系
组合是has关系
class Person() {}
class Book(){}
public class Student extends Person(
private Book book;
){}
package com.day.one; public class Main { public static void main(String[] args) { Person p = new Person("原始人"); Student s = new Student("李白"); s.readBood(); p.readBood(); s.foot = "大脚"; System.out.println(s.foot); // upcasting Person ps = new Student("dufu"); Object o = p; // downcasting // 向下转型前可以用instanceof判断 if(p instanceof Student){ Student s1 = (Student)p; } System.out.println(p instanceof Person); System.out.println(p instanceof Student); } }
package com.day.one; public class Student extends Person { public Student(String name) { super(name); // TODO Auto-generated constructor stub } private int scroe; public int getScroe() { return scroe; } public void setScroe(int scroe) { this.scroe = scroe; } }
package com.day.one; public class Person { private String name; private int age; protected String foot; public Person(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name.trim(); } public void setName(String firstName, String lastName) { this.name = firstName + " | " + lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void readBood() { System.out.println(this.getName()+ "读书"); } }
子类覆写父类的方法是覆写(overide)
方法签名如果不同就不是overide,而是创建一个新方法
(方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。
注意,方法签名不包括方法的返回类型。不包括返回值和访问修饰符。)
可以使用注解@Overide让编译器检测是否正确的覆写
引用变量的声明类型可能与实际类型不符。
实例对象的方法调用总是对应实际类型。
Java的实例方法调用是基于运行时实际类型的动态调用。
public void runTwice(Person p){
p.run();
p.run();
}
从上面的代码,无法知道是否调用Person类的定义的 run()方法,还是他的父类还是他的子类。
多态是指 针对 某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
对某个类型调用某个方法,执行的方法可能是某个子类的覆写方法。
利用多态,允许添加更多类型的子类实现功能扩展。
Object定义的几个重要方法:
toString: 把instance输出为String
equals:判断连个instance是否逻辑相等
hasCode:计算一个instance的哈希值
用final修饰的方法不能被override
用final修饰的类不能被继承
用final修饰的字段在初始化不能被修改
package com.day.one; public class Main { public static void main(String[] args) { Person p = new Person("原始人"); Student s = new Student("李白"); // Java的实例方法调用是基于运行时实际类型的动态调用。 System.out.println(s.readBook()); System.out.println(p.readBook()); // 实际调用Object类的toString 打印出Person在jvm的引用地址 System.out.println(p); } }
package com.day.one; public class Student extends Person { public Student(String name) { super(name); // TODO Auto-generated constructor stub } private int scroe; public int getScroe() { return scroe; } public void setScroe(int scroe) { this.scroe = scroe; } @Override public String readBook() { // super可以调用父类的被 重写的方法 return super.readBook() + "!!!"; } }
package com.day.one; public class Person { private String name; private int age; protected String foot; public Person(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name.trim(); } public void setName(String firstName, String lastName) { this.name = firstName + " | " + lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String readBook() { return this.name + "读书"; } }
每个子类都可以覆写父类的方法。如果父类的方法没有实际意义,能否去掉方法的执行语句?
不能,如果去掉父类的方法,就会失去多态的特性,可以把父类声明抽象类和父类方法声明抽象方法。
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法:
抽象方法用abstract修饰
抽象方法没有任何执行语句
因为无法执行抽象方法,因为这个类也必须申明为抽象类。
无法实例化一个抽象类
抽象类用于被继承
抽象类可以强迫子类实现其定义的抽象方法(否则编译错误)
抽象方法实际上相当于定义了“规范”
如果不实现抽象方法,则该子类仍是抽象类。
面向抽象编程的本质:
上层代码只定义规范
不需要子类就可以实现业务逻辑
具体的业务逻辑由不同子类实现,调用者并不关心
package com.day.two; public class Cirle extends Shape { private final double radius; public Cirle(double radius) { this.radius = radius; } @Override public double area() { // TODO Auto-generated method stub return Math.PI * radius * radius; } }
package com.day.two; public class Rect extends Shape { private final double width; private final double height; public Rect(double width, double height) { this.width = width; this.height = height; } @Override public double area() { // TODO Auto-generated method stub return this.height * this.width; } }
package com.day.two; public abstract class Shape { public abstract double area(); }
package com.day.two; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Rect rect = new Rect(12,12); Cirle cirle = new Cirle(5); System.out.println(rect.area()); System.out.println(cirle.area()); } }
如果一个抽象类没有字段,所有的方法都是抽象方法,就可以把抽象类改写为接口。
使用interface声明一个接口。
接口默认定义方法是public abstract
Interface是java内置的纯对象接口。
实现interface使用implements
可以实现多个interface
注意区分术语
Java的接口特指interface定义的接口,只定义方法签名
编程的接口泛指接口规范,如方法签名,数据格式,网络协议等
|
abstract class |
interface |
继承 |
单继承 |
多接口 |
字段 |
可以定义实例字段 |
不可以定义实例字段 |
抽象方法 |
Yes |
Yes |
非抽象方法 |
可以定义非抽象方法 |
可以定义default方法 |
一个接口可以继承另一个接口,使用extends, 相当于扩展的接口的方法
合理设计interface和abstract class的继承关系
公共逻辑放在abstract class
接口层次代表抽象程度。
package com.day.two; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Rect rect = new Rect(12,12); Cirle cirle = new Cirle(5); System.out.println(rect.area()); System.out.println(cirle.area()); } }
package com.day.two; public class Cirle implements Shape { private final double radius; public Cirle(double radius) { this.radius = radius; } @Override public double area() { // TODO Auto-generated method stub return Math.PI * radius * radius; } @Override public String hehe() { // TODO Auto-generated method stub return null; } }
package com.day.two; public class Rect implements Shape { private final double width; private final double height; public Rect(double width, double height) { this.width = width; this.height = height; } @Override public double area() { // TODO Auto-generated method stub return this.height * this.width; } public String hehe() { return "rect"; } }
package com.day.two; public interface Shape extends Shape2 { double area(); default double zhouchang() { return 0; } }
package com.day.two; public interface Shape2 { String hehe(); }
用static修饰的字段称为静态字段
普通字段在每个实例中都有自己一个独立空间
静态字段只有一个共享的空间,所有的实例都共享该字段
不推荐用实例变量访问静态字段
推荐用类名访问静态字段(因为在java程序中,实例对象并没有静态字段,代码中实例对象能访问静态字段,是因为编译器根据实例自动转换类名来访问静态字段)
可以把静态字段理解为描述class本身的字段(非实例字段)
用static修饰的方法称为静态方法
调用实例方法必须通过实例变量
调用静态方法不需要实例变量
静态方法类似其他编程语言的函数
静态方法不能访问this变量
静态方法不能访问实例字段
静态方法可以访问静态字段和静态方法
静态方法通常用于工具类
Array.sort()
Math.random
静态方法经常用于辅助方法
Java入口程序main()也是静态方法
package com.day.there; public class Main { public static void main(String[] args) { Person p1 = new Person("li"); System.out.println(Person.getNumber()); Person p2 = new Person("li"); System.out.println(Person.getNumber()); Person p3 = new Person("li"); System.out.println(Person.getNumber()); } }
package com.day.there; public class Person { private String name; private static int num; public Person(String name) { this.name = name; Person.num++; } public static int getNumber(){ return Person.num; } }
Java定义名字空间:包
包名+类名 = 完整类名
Jvm只看完整类名,只要包名不同,类就不同
包可以是多层结构
包没有父子关系(java.util和java.util.zip是不同的包)
位于同一个包的类,可以访问包作用域的字段和方法:
不用public,private,protected修饰的字段和方法就是包作用域
先使用完整类名
先import,再使用类名
可以使用*(不推荐)
Java的类,字段,方法,接口都可以设置访问权限
访问权限是指在一个类的内部,能否引用另一个类以及访问它的字段和方法
访问权限有public,private,protected,package
定义为public的class,interface,field,method可以被其他类访问
定义为private的field,method不可以被其他类访问
Private访问权限限定在class的内部,与方法生命顺序无关
定义为private的class无法被其他类访问
访问private class 被限定在外层class的内部
定义在一个class内部的class称为内部类
Protected作用于继承关系
定义protected的字段和方法可以被子类访问
包作用域是指一个类允许访问同一个package的
没有public,private修饰的class
没有public,protected,private修饰的字段和方法
包名必须完全一致
在方法内部定义的变量称为局部变量
局部变量作用域从变量声明出开始到对应的块结束
尽可能把局部变量的作用域缩小
尽可能延后生命局部变量
Final与访问权限不冲突
用final修饰class可以阻止被继承
用final修饰method可以阻止被覆写
用final修饰filed可以被重新赋值
用final修饰局部变量可以阻止被重新赋值
classpath是一个环境变量
classpath指示jvm如何搜索class
classpath设置的搜索路径与操作系统相关:window下是一分号分隔,linux是以冒号分隔
classpath设置方法:
在系统环境中设置(不推荐,在系统设置的classpath会干扰到特定程序)
启动JVM时用-classpath或-cp设置(推荐)
Eclipse自动传入当前工程bin目录作为classpath
jar包是zip格式的压缩文件,包含若干class文件
jar包相当于目录
使用jar包来避免大量目录和class文件
创建jar包:
l JDK的jar命令
l Maven等工具
l 压缩为zip然后改名为jar
jar包可以有一个特殊的/META-INF/MANIFEST.MF文件来指定Main-Class
MANIFEST.MF是纯文本,可以指定Main-class和其他信息
Jar包还可以包含其他jar包
Jvm运行时会自动加载jdk自带的class
Jdk自带的class被打包在rt.jar包
不需要在classpath中引用rt.jar包,jvm会自动加载
可以直接使用”…”
内容不可变
比较String内容是否相等: equals(Object)
比较String 不区分大小写: equalsgnoreCase(String)
是否包含子串:boolean contains(CharSequence)
返回子串的索引位置 int indexOf(String)
向后查找: int lastIndexOf(String)
查找字符串有某个位置开始:boolean startsWith(String)
查找字符串有莫个位置结束: Boolean endsWith(String)
trim()
移除首尾空白字符
空格, \t \r \n
注意:trim()不改变字符串内容,而是返回新的字符串
提取子串,没有改变原字符串,返回新串 substring()
大小写切换:toUpperCase() toLowerCase()
替换一个字符或一串字符: replace
使用正则替换:replaceAll(String, String)
分割字符串:String[] split(String)
拼接字符串:static String.join()
任意数据转换string
static String.valueOf(int|Object|boolean)
把string转换其他类型
static int Integer.parseInt(String)
static Integer Integer.valueOf(String)
String转换为char[] char[] toCharArray()
Char[]转换为String new String(char[])
String转换为byte[] byte[] getBytes(“”) // 默认使用系统编码,window默认编码gbk,linux默认的utf8 不推荐使用
Byte[]转换String
New String(byte[], String)
New String(byte[], Charset)
Java使用Unicode编码
输入输出时把String和byte[]转换,需要考虑编码
始终优先考虑utf-8编码
String可以使用+拼接
每次循环都会创建新的字符串对象
绝大部分都是临时对象,浪费内存
影响GC效率
StringBuilder可以高效拼接字符串
l StringBuilder是可变对象
l StringBuilder可以预分配缓冲区
StringBuilder可以进行链式操作
当只有一行代码:不需要特别改写字符串+操作
编译器在内部自动把多个连续的+操作优化为StringBuilde操作
StringBuffer是StringBuilder的线程安全版本,很少使用
Jdk为每种基本类型都创建了对应的包装类型
基本类型 |
对应的应用类型 |
boolean |
Boolean |
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
char |
character |
int,Integer和String的相互转换
int i = 100;
Integer n1 = new Integer(i);
Integer n2 = Integer.valueOf(i);
Integer n3 = Integer.valueOf(“100”);
int x1 = n1.intValue();
int x2 = Integer.parseInt(“100”);
特别注意Integer.getInteger(String)是从系统环境中读取系统变量
发生在编译阶段
装箱和拆箱会影响执行效率
编译后的class代码是严格区分基本类型和引用类型
java包装类型全部继承Number 这个类
可以利用向上转型变成Number对象,然后通过Number通过方法转换为基本类型
l 若个字段private实例字段
l 通过public方法读写实例字段
符合命名规范的class被称为JavaBean
通常把一组对应的getter和setter称为属性
只有getter的属性成为只读属性
只有setter的属性成为只写属性
属性只需要定义getter/setter方法,不一定需要对应的字段
使用Introspector.getBeanInfo()获取属性列表
方便ide工具读取属性
传递数据
枚举属性
用enum定义常量:
关键字enum定义常量类型
常量本身带有类型信息
使用==比较
enum定义的类型实际上是class
继承自java.lang.Enum
不能通过new创建实例
所有常量都是唯一的实例(引用类型)
可以用switch语句
原文:https://www.cnblogs.com/lzy007/p/9126284.html