1、抽象方法和抽象类
1.1 基本概念
- 抽象方法:使用abstract修饰的方法,只有声明,没有方法体(实现)。
- 抽象类:包含抽象方法的类就是抽象类。
- 抽象类存在的意义:定义的是一种规范,告诉子类必须要给抽象方法提供具体的实现,可以做到严格限制子类的这设计,是子类之间更加通用。
//抽象类
abstract class Animal {
//抽象方法
abstract public void cry();
}
1.2 抽象类的使用要点
- 有抽象方法的类只能定义成抽象类。
- 抽象方法只定义,不实现,但要求子类必须实现。
- 抽象类不能使用new来实例化。
- 抽象类只能用来被继承。
- 抽象类可以包含属性、方法、构造方法。但构造方法不能用来new实例,只能用来被子类调用。
public class TestAbstract {
public static void main(String[] args) {
// Animal dog = new Animal(); //错误:抽象类不能实例化
Animal dog = new Dog();
//调用抽象方法时构造方法也会被调用
dog.cry(); //叫......
//汪汪汪......
dog.see(); //看门......
}
}
//抽象类
abstract class Animal {
//抽象方法
abstract public void cry();
int age;
public Animal() {
System.out.println("叫......");
}
public void see() {
System.out.println("看门......");
}
}
class Dog extends Animal {
@Override
public void cry() {
System.out.println("汪汪汪......");
}
}
2、接口
2.1 接口的基本概念
接口是比抽象类更抽象的抽象类,可以更加规范的对子类进行约束,实现了:规范和具体实现的分离。接口不提供任何实现,接口中的方法都是抽象方法。
2.2 定义接口
[访问权限修饰符] interface 接口名 [extends 父接口1,父接口2...] {
常量定义;
方法定义;
}
定义接口相关说明:
(1)访问权限修饰符:只能是public或默认;
(2)常量:接口中的属性只能是常量,总是由public static final 修饰,不写的话系统也会自动加上;
(3)方法:接口中的方法只能是抽象方法,总是由public abstract修饰,不写的话系统也会自动加上;
(4)接口可以多继承。
2.3 接口的实现
- java中通过implements来实现接口。
- 接口不能创建实例。
- 一个类实现了接口,必须实现接口中的所有方法,并且这些方法只能是public的。
public class TestInterface {
public static void main(String[] args) {
Test t = new Student();
t.study();
t.play();
}
}
//接口
interface Test {
/*public static final*/ int MAX = 123;
/*public abstract*/ void study();
void play();
}
//实现类
class Student implements Test {
@Override
public void study() {
System.out.println("学习");
}
@Override
public void play() {
System.out.println("玩");
}
}
3、内部类
Java中内部类分为:成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。
3.1 非静态内部类
非静态内部类特点:
- 非静态内部类必须寄存在一个外部类对象中(非静态内部类依托外部类)。
- 非静态内部类可以直接访问外部类成员,但外部类不能直接访问费静态内部类成员(提供一个很好的封装)。
- 非静态内部类不能有静态方法、属性和初始化块。
非静态内部类成员变量访问要点:
- 外部类属性:外部类名.this.变量名
- 内部类属性:this.变量名
- 内部类里方法的局部变量:变量名
- 创建非静态内部类对象:
new 外部类().new 内部类()
public class TestInner {
public static void main(String[] args) {
//创建内部类对象
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
class Outer {
int age = 15;
//非静态内部类
class Inner {
int age = 25;
public void show() {
int age = 35;
System.out.println("内部方法的局部变量age = " + age);
System.out.println("内部类属性age = " + this.age);
System.out.println("外部类属性age = " + Outer.this.age);
}
}
}
3.2 静态内部类
- 静态内部类不依托于外部类。
- 静态内部类可以看做外部类的一个静态成员。
- 静态内部类不能访问外部类属性。
public class TestStaticInner {
public static void main(String[] args) {
//创建内部类对象
Outer2.Inner2 inner2 = new Outer2.Inner2();
inner2.show();
}
}
class Outer2 {
//静态内部类,相当于外部类的一个静态成员
int age = 45;
static class Inner2 {
int age = 100;
public void show() {
int age = 36;
System.out.println("内部方法的局部变量age = " + age);
System.out.println("内部类属性age = " + this.age);
//静态内部类不能访问外部类属性
// System.out.println("外部类属性age = " + Outer.this.age);
}
}
}
3.3 匿名内部类
- 匿名内部类适合那种只需要使用一次的类。如:键盘监听操作等。
- 匿名内部类没有访问权限修饰符。
- 匿名内部类没有构造方法(因为它连名字都没有)。
- 匿名内部类语法:
new 父类构造器() / 实现接口() {
匿名内部类类体
}
public class TestAnonymousInner {
public static void test(AA a) {
a.aa();
}
public static void main(String[] args) {
//匿名内部类
TestAnonymousInner.test(new AA() {
@Override
public void aa() {
System.out.println("TestAnonymousInner.main(...).new AA() {...}.aa()");
}
});
}
}
interface AA {
void aa();
}
3.4 局部内部类
- 定义在方法内部的类,作用域只限于本方法。在实际开发中应用很少。
public class TestLocalInner {
public void show3() {
//局部内部类:只限于方法内部使用
class Inner3 {
public void fun() {
System.out.println("TestLocalInner.show3().Inner3.fun()");
}
}
new Inner3().fun();
}
public static void main(String[] args) {
new TestLocalInner().show3();
}
}
4、String
String类又称作不可变字符序列,位于java.lang中。
4.1 String类的基本用法
public class TestString {
public static void main(String[] args) {
String a1 = "";
String a2 = "apple";
String a3 = new String("apple");
String a4 = "app" + "le";
String a5 = "87" + 74; //8774
System.out.println(a2 == a4); //true
//比较字符串使用equals,比较内容是否相同
System.out.println(a2.equals(a3)); //true
}
}
java中常量池的分类:
- 全局字符串常量池
- class文件常量池
- 运行时常量池
4.2 String类的常用方法
通过jdk api可以查看java中各个类所包含的方法。
public class TestString01 {
public static void main(String[] args) {
String s1 = "core Java";
String s2 = "Core Java";
//字符串长度
System.out.println(s1.length());
//提取字符串
System.out.println(s1.charAt(6)); //提取下标为6的字符
System.out.println(s2.substring(5)); //从下标为5开始提取字符
System.out.println(s2.substring(0, 4)); //提取下标[0,4)的字符
//比较字符串
System.out.println(s1.equals(s2)); //区分大小写,false
System.out.println(s1.equalsIgnoreCase(s2)); //不区分大小写,true
//检测字符串是否包含某字符串
System.out.println(s1.indexOf("Java")); //区分大小写,包含返回字符串的开始下标,否则返回-1
//替换字符串
String s3 = s1.replace(" ", "&");
System.out.println(s3); //core&Java
//判断字符串是否以某个字符串开头或结尾
System.out.println(s3.startsWith("core")); //true
System.out.println(s3.endsWith("java")); //false
//大小写转换
System.out.println(s1.toLowerCase()); //大写转小写,core java
System.out.println(s1.toUpperCase()); //小写转大写,CORE JAVA
//去掉字符串首尾的空格
String s4 = " Hello World!!! ";
System.out.println(s4.trim()); //Hello World!!!
System.out.println(s4); // Hello World!!!
}
}
5、数组的拷贝、插入、删除以及Arrays工具类
5.1 数组拷贝
public class TestArrayCopy {
public static void main(String[] args) {
String[] c1 = {"aa","bb","cc","dd","ee"};
String[] c2 = new String[5];
System.arraycopy(c1, 1, c2, 2, 2);
for (int i = 0; i < c2.length; i++) {
System.out.println(i + " ---> " + c2[i]);
}
}
}
System.arraycopy(src, srcPos, dest, destPos, length);
src:从哪个数组拷贝
srcPos:从哪个下标开始拷贝
dest:拷贝到哪个数组
destPos:从哪个下标开始存放
length:拷贝的长度
5.2 数组元素删除
- 本质上也是数组的拷贝
//c1表示需要操作的数组,index表示需要删除元素的下标
public static String[] removeArray(String[] c1,int index) {
System.arraycopy(c1, index+1, c1, index, c1.length-index-1);
c1[c1.length-1] = null;
for (int i = 0; i < c1.length; i++) {
System.out.println(i + " ---> " + c1[i]);
}
return c1;
}
5.3 数组元素插入
- 先进行数组的扩容,然后拷贝元素,最后再将新的元素插入。
public static String[] insertArray(String[] c1,String str) {
//数组扩容
String[] c3 = new String[c1.length+1];
//数组拷贝
System.arraycopy(c1, 0, c3, 0, c1.length);
//插入数组元素
c3[c3.length-1] = str;
for (int i = 0; i < c3.length; i++) {
System.out.println(i + " ---> " + c3[i]);
}
return c3;
}
5.4 Arrays工具类
jdk提供的java.util.Arrays类,包含了常用的数字操作。
Array工具类常用的方法:
public class TestArrays {
public static void main(String[] args) {
int[] arr = {12,36,11,54,87,96,25};
//打印数组
System.out.println(Arrays.toString(arr));
//排序(从小到大)
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//查找指定元素
System.out.println(Arrays.binarySearch(arr, 12));
}
}
5.5 多维数组
public class TestArray {
public static void main(String[] args) {
String[][] a1 = new String[2][3];
for (String[] strings : a1) {
System.out.println(Arrays.toString(strings));
}
System.out.println("#######################");
int[][] a2 = {
{100,200},
{200,300,400},
{300,400,500,600}
};
for (int[] is : a2) {
System.out.println(Arrays.toString(is));
}
}
}
使用数组存储表格内容:
姓名 | 性别 | 民族 | 年龄 |
---|---|---|---|
张三 | 男 | 汉族 | 25 |
李四 | 女 | 蒙古族 | 36 |
王五 | 男 | 哈萨克族 | 17 |
public class Test2Array {
public static void main(String[] args) {
Object[] temp0 = {"姓名","性别","民族","年龄"};
Object[] temp1 = {"张三","男","汉族",25};
Object[] temp2 = {"李四","女","蒙古族",36};
Object[] temp3 = {"王五","男","哈萨克族",17};
Object[][] temps = new Object[4][];
temps[0] = temp0;
temps[1] = temp1;
temps[2] = temp2;
temps[3] = temp3;
for (Object[] objects : temps) {
System.out.println(Arrays.toString(objects));
}
}
}
6、常用的排序和查找算法
6.1 冒泡排序
- 相邻元素进行比较
public static void Bubble(int[] a) {
for (int i = 0; i < a.length-1; i++) {
for (int j = 0; j < a.length-1-i; j++) {
if (a[j] > a[j+1]) {
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(a));
}
6.2 二分法查找(折半查找)
基本思想:
1、首先将数组中的元素从小到大进行排序
2、将需要查找的值value与数组中间位置上的值midValue进行比较:
- 如果value = midValue,检索成功;
- 如果value > midValue,在数组后半部分继续进行二分法查找;
- 如果value < midValue,在数组前半部分继续进行二分法查找;
- 如果检索结束未找到,则检索失败。
public class BinarySearch {
public static void main(String[] args) {
int[] a = {2,5,4,1,6,3,9,8,7};
System.out.println(Binary(a, 70));
}
public static int Binary(int[] a,int value) {
//从小到大排序
Arrays.sort(a);
int low = 0;
int high = a.length -1;
while(low <= high) {
int mid = (low + high) / 2;
//检索成功
if (value == a[mid]) {
return mid;
}
//未检索到,继续检索
if (value > a[mid]) {
low = mid + 1;
}
if (value < a[mid]) {
high = mid - 1;
}
}
//检索失败
return -1;
}
}