0%

一、容器

1.1 介绍

  • 数组本身是一种容器,劣势是不灵活,长度固定,方法有限,只能存储有序、可重复的数据
  • 容器:在Java当中,如果有一个类专门用来存放其它类的对象,这个类就叫做容器,或者就叫做集合,集合就是将若干性质相同或相近的类对象组合在一起而形成的一个整体
  • 从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等

1.2 接口方法

1.2.1 Collection

以下方法都是见名知意,就不做解释了

方法
size():int
isEmpty():boolean
contains(Object):
iterator():iterator\
toArray():Object[]
add(E):boolean
remove(Object):boolean
containsAll(Collection<?>):boolean
addAll(Collection<? extends E>):boolean
removeAll(Collection<?>):boolean
clear():void

1.2.2 Map

  • 以下方法都是见名知意,就不做解释了
方法
size():int
isEmpty():boolean
containsKey(Object):boolean
containsValue(Object):boolean
get(Object):V
put(K,V):V
remove(Object):V
putAll(Map<? extends K,?extends V>):void
clear():void
ketSet():Set\
values():Collection\
equals(Object):boolean

二、泛型

2.1 基础

  • 将数据类型作为参数,进行变量的声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// <>,可以用T,E,V表示,代表需要接受的数据类型的参数
// 可以通过MyCollect<String> mc=new MyCollection<String>();创建对象
class MyCollect<E>{
Object[] objs = new Object[5];

public void set(E e,int index){
objs[index] = e;
}

public E get(int index){
return (E)objs[index];
}
}

2.2 泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}

public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

System.out.println( "整型数组元素为:" );
printArray( intArray ); // 传递一个整型数组

System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray ); // 传递一个双精度型数组

System.out.println( "\n字符型数组元素为:" );
printArray( charArray ); // 传递一个字符型数组
}
}

/*Output:
整型数组元素为:
1 2 3 4 5

双精度型数组元素为:
1.1 2.2 3.3 4.4

字符型数组元素为:
H E L L O
*/

2.2.1 有界的类型参数

  • 可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。

  • 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class MaximumTest
    {
    // 比较三个值并返回最大值
    public static <T extends Comparable<T>> T maximum(T x, T y, T z)
    {
    T max = x; // 假设x是初始最大值
    if ( y.compareTo( max ) > 0 ){
    max = y; //y 更大
    }
    if ( z.compareTo( max ) > 0 ){
    max = z; // 现在 z 更大
    }
    return max; // 返回最大对象
    }
    public static void main( String args[] )
    {
    System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
    3, 4, 5, maximum( 3, 4, 5 ) );

    System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
    6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );

    System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
    "apple", "orange", maximum( "pear", "apple", "orange" ) );
    }
    }
    /*Output:
    3, 4 和 5 中最大的数为 5

    6.6, 8.8 和 7.7 中最大的数为 8.8

    pear, apple 和 orange 中最大的数为 pear
    */

2.3 泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Box<T> {

private T t;

public void add(T t) {
this.t = t;
}

public T get() {
return t;
}

public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();

integerBox.add(new Integer(10));
stringBox.add(new String("菜鸟教程"));

System.out.printf("整型值为 :%d\n\n", integerBox.get());
System.out.printf("字符串为 :%s\n", stringBox.get());
}
}

/*Output:
整型值为 :10

字符串为 :菜鸟教程
*/

2.4 类通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.*;

public class GenericTest {

public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();

name.add("icon");
age.add(18);
number.add(314);

getData(name);
getData(age);
getData(number);

}

public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
/*Output:
data :icon
data :18
data :314
*/
  • 类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
  • 在(//1)处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错

  • 类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import java.util.*;

    public class GenericTest {

    public static void main(String[] args) {
    List<String> name = new ArrayList<String>();
    List<Integer> age = new ArrayList<Integer>();
    List<Number> number = new ArrayList<Number>();

    name.add("icon");
    age.add(18);
    number.add(314);

    //getUperNumber(name);//1
    getUperNumber(age);//2
    getUperNumber(number);//3

    }

    public static void getData(List<?> data) {
    System.out.println("data :" + data.get(0));
    }

    public static void getUperNumber(List<? extends Number> data) {
    System.out.println("data :" + data.get(0));
    }
    }

    /*Output:
    data :18
    data :314
    */

三、注解

3.1 注解的定义

  • 注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

3.2 注解的作用

  • 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;
  • 在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  • 跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
  • 在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。可以在反射中解析并使用 Annotation。

3.3 注解类的写法

1
2
3
public @interface MyTestAnnotation{

}
  • 作用在类上
    1
    2
    3
    4
    5
    6
    7
    @MyTestAnnotation
    public class test{
    @MyTestAnnotation
    public static void main(String[] args){

    }
    }

3.4 元注解

  • 元注解:作用在注解上的 注解,方便实现注解想要的功能
  • 元注解有五种
    1
    2
    3
    4
    5
    @Retention
    @Target
    @Document
    @Inherited
    @Repeatable

    3.4.1 @Retention

  • Retention:有保留保持的意思,表示注解存在的阶段是保留在源码(编译期),字节码(类加载),或者运行期(jvm中运行),使用RetentionPolicy来表示注解的保留时期
  • 一般使用RetentionPolicy.RUNTIME
    1
    2
    3
    @Retention(RetentionPolicy.SOURCE) // 尽存在于源码中,class字节码中不存在
    @Retention(RetentionPolicy.CLASS) // class字节码文件中存在,运行时无法获得
    @Retention(RetentionPolicy.RUNTIME) // 在class字节码文件中存在,运行时可通过反射获得

3.4.2 @Target

  • Target:目标的意思,表示设置注解作用的目标范围;通过ElementType表达作用类型
  • 一般使用ElementType.TYPE
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Target(ElementType.TYPE) // 作用于接口、枚举、注解
    @Target(ElementType.FIELD) // 作用于属性字段、枚举的常量
    @Target(ElementType.METHOD) // 作用于方法
    @Target(ElementType.PARAMETER) // 作用于方法参数
    @Target(ElementType.CONSTRUCTOR) // 作用于构造函数
    @Target(ElementType.LOCAL_VALUABLE) // 作用于局部变量
    @Target(ElementType.ANNOTATION_TYPE) // 作用于注解
    @Target(ElementType.PACKAGE) // 作用于包
    @Target(ElementType.TYPE_PARAMETER) // 作用于类型泛型,及泛型类、泛型方法、泛型接口
    @Target(ElementType.TYPE_USE) // 可以标注任意类型,除了class

3.4.3 @Documented

  • Document是文档的意思,作用是将注解中的元素包含到Javadoc中去

3.4.4 @Inherited

  • Inherited是继承的意思
  • 一个被该注解 修饰的类,如果其子类没有被其他注释修饰,则它的子类也继承了父类的注解
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @Documented
    @Inherited
    @Retention(RetentPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MyTestAnnotation{

    }

    @MyTestAnnotation
    public class Father{

    }

    public class Son extends Father{

    }

    public class test{
    public static void main(String[] args){
    // 获取Son的class对象
    Class sonClass = Son.class;

    // 获取Son类上的注解MyTestAnnotation
    MyTestAnnotation annotation = sonClass.getAnnotation(MyTestAnnotation.class);
    }
    }

    3.4.5 @Repeatable

  • Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface People {
    Game[] value() ;
    }

    @Repeatable(People.class)
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Game {
    String value() default "";
    }

    @Game(value = "LOL")
    @Game(value = "PUBG")
    @Game(value = "NFS")
    @Game(value = "Dirt4")
    public class PlayGame {
    }

  • 通过上面的例子,你可能会有一个疑问,游戏注解中括号的变量是啥,其实这和游戏注解中定义的属性对应。接下来我们继续学习注解的属性。

3.5 注解的属性

  • 通过上一小节@Repeatable注解的例子,我们说到注解的属性。注解的属性其实和类中定义的变量有异曲同工之处,只是注解中的变量都是成员变量(属性),并且注解中是没有方法的,只有成员变量,变量名就是使用注解括号中对应的参数名,变量返回值注解括号中对应参数类型。相信这会你应该会对上面的例子有一个更深的认识。而@Repeatable注解中的变量则类型则是对应Annotation(接口)的泛型Class
1
2
3
4
5
6
7
8
9
10
11
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the containing annotation type for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}

3.6 注解的本质

  • 注解的本质就是一个Annotation接口
    1
    2
    3
    4
    5
    6
    7
    8
    public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    Class<? extends Annotation> annotationType();
    }
  • 通过以上源码,我们知道注解本身就是Annotation接口的子接口,也就是说注解中其实是可以有属性和方法,但是接口中的属性都是static final的,对于注解来说没什么意义,而我们定义接口的方法就相当于注解的属性,也就对应了前面说的为什么注解只有属性成员变量,其实他就是接口的方法,这就是为什么成员变量会有括号,不同于接口我们可以在注解的括号中给成员变量赋值。

3.7 注解属性类型

3.7.1 注解属性类型可以有以下列出的类型

  1. 基本数据类型
  2. String
  3. 枚举类型
  4. 注解类型
  5. Class类型
  6. 以上类型的一维数组类型

3.8 注解成员变量赋值

  • 如果注解又多个属性,则可以在注解括号中用“,”号隔开分别给对应的属性赋值,如下例子,注解在父类中赋值属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Documented
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MyTestAnnotation {
    String name() default "mao";
    int age() default 18;
    }

    @MyTestAnnotation(name = "father",age = 50)
    public class Father {
    }

3.9 获取注解属性

  • 前面我们说了很多注解如何定义,放在哪,现在我们可以开始学习注解属性的提取了,这才是使用注解的关键,获取属性的值才是使用注解的目的。
  • 如果获取注解属性,当然是反射啦,主要有三个基本的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }


    public A getAnnotation(Class annotationClass) {
    Objects.requireNonNull(annotationClass);

    return (A) annotationData().annotations.get(annotationClass);
    }

    public Annotation[] getAnnotations() {
    return AnnotationParser.toArray(annotationData().annotations);
    }
  • 下面结合前面的例子,我们来获取一下注解属性,在获取之前我们自定义的注解必须使用元注解@Retention(RetentionPolicy.RUNTIME)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public class test {
    public static void main(String[] args) throws NoSuchMethodException {

    /**
    * 获取类注解属性
    */
    Class fatherClass = Father.class;
    boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
    if(annotationPresent){
    MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
    System.out.println(annotation.name());
    System.out.println(annotation.age());
    }

    /**
    * 获取方法注解属性
    */
    try {
    Field age = fatherClass.getDeclaredField("age");
    boolean annotationPresent1 = age.isAnnotationPresent(Age.class);
    if(annotationPresent1){
    Age annotation = age.getAnnotation(Age.class);
    System.out.println(annotation.value());
    }

    Method play = PlayGame.class.getDeclaredMethod("play");
    if (play!=null){
    People annotation2 = play.getAnnotation(People.class);
    Game[] value = annotation2.value();
    for (Game game : value) {
    System.out.println(game.value());
    }
    }
    } catch (NoSuchFieldException e) {
    e.printStackTrace();
    }
    }
    }

    3.9.1 运行结果

3.10 注解的作用

  • 提供信息给编译器: 编译器可以利用注解来检测出错误或者警告信息,打印出日志。
  • 编译阶段时的处理:软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理。
  • 运行时处理: 某些注解可以在程序运行的时候接受代码的提取,自动做相应的操作。
  • 正如官方文档的那句话所说,注解能够提供元数据,转账例子中处理获取注解值的过程是我们开发者直接写的注解提取逻辑,处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。上面转账例子中的processAnnotationMoney方法就可以理解为APT工具类

四、IO

4.1 类结构

  • 节点流:可以直接从数据源或目的地读写数据的数据流
  • 处理流(包装流):对节点流操作的优化

4.2 操作文件

4.2.1 File类

  • 作用:文件和目录名的抽象表示

  • 四种构造方法

1
2
3
4
new File(File parent,String child);
new File(String pathname);
new File(String parent, String child);
new File(URI uri);
  • 获取文件路径方法:
1
2
3
4
5
6
7
String path=""; //绝对或相对路径,相对路径是相对改项目文件
File src=new File(path);
src.getName(); //获取文件或目录的名称;返回String
src.getPath(); //获取构建的给定的路径;返回String
src.getAbsolutePath(); //获取绝对路径;返回String
src.getParent(); //获取构建的父级目录字符串;返回String,无返回null
src.length(); //返回文件大小,单位为K,目录或文件不存在返回0;返回long
  • 获取文件状态方法:
1
2
3
4
5
String path="";
File src=new File(path);
src.exists(); //判断文件存在;返回boolean
src.isFile(); //判断是否为一个文件;返回boolean
src.isDirectory(); //判断是否为一个目录;返回boolean
  • 其他操作:
1
2
3
4
5
6
7
8
String path="";
File src=new File(path);
src.createNewFile(); //该文件不存在的情况下,创建一个空的新文件或文件夹;返回boolean
src.delete() //删除操作;返回boolean
src.mkdir(); //上级目录不存在则创建失败;返回boolean
src.mkdirs(); //上级目录可以不存在(recommend)
src.list(); //列出下级名称;返回String
src.listFiles(); //返回下级文件对象列表;返回File[]

4.2.2 文件编码

  • 编码:字符—>字节
  • 解码:字节—>字符
字符集 说明
ASCII 英文字符集
ISO-8859-1 包含中,日,拉丁字符
UTF-8 变长字符集(1-3个字节)
UTF-16BE 定长字符集(2个字节),大端存储
UTF-16LE 定长字符集(2个字节),小段存储
UTF-16 文件开头指定大端表示还是小段表示
GBK 中文字符集
  • 编码函数

    1
    2
    3
    String msg=""
    byte[] data = msg.getBytes(); //默认为项目的编码方式
    byte[] data = msg.getBytes("GBK"); //指定编码方式
  • 解码函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    String msg=""
    byte[] data = msg.getBytes();

    msg = new String(data,0,data.length,"utf-8"); //通过构造器进行解码操作
    /*
    * 第一个参数是数组引用
    * 第二个是解码的起始位置
    * 第三个是需要解码的字节数
    * 第四个是按照什么方式解码
    */
  • 乱码原因

    • 给定的字节数不够,第三个参数
    • 字符集不符合,第四个参数

五、节点流

5.1 读写操作

  • 节点流可以对文件进行读写操作,一般的读写流程如下:
    • 创建源
    1. 选择流
    2. 操作
    3. 释放
  1. 节点流分为字节流字符流

5.2 字节流

5.2.1 输入流FileInputStream

(1)read函数读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// TODO Auto-generated method stub
int temp;
// 创建源
File file=new File("abc.txt"); // 内容为hello world

// 选择流
FileInputStream input=null;
input=new FileInputStream(file);

// 读取操作,read()读到文件末尾返回-1
while((temp=input.read())!=-1) {
System.out.print((char)temp);
}

// 关闭流
if(input!=null)
input.close();

(2)分段读取

1
2
3
4
5
6
7
8
9
input=new FileInputStream(file);
// 读取操作,分段读取
byte[] flush = new byte[1024] // 缓冲容器
int len=-1;
while((temp=input.read(flush))!=-1) {
// 字节数组-->字符串(解码)
String str=new String(flush,0,len);
System.out.print(str);
}

5.2.2 输出流

  • 写文件分两类
    • 覆盖
    • 追加
  • 注:要注意添加异常操作
1
2
3
4
5
6
7
// 若进行追加,new FileOutputStream(file,true);
// 第二个参数表示是否追加
OutputStream os=new FileOutputStream(file);
String msg="";
byte[] data=msg.getBytes(); //编码
os.write(data,0,data.length);
os.flush();

5.2.3 文件拷贝

  • read(flush)返回读取字节的长度
    1
    2
    3
    4
    5
    6
    // in为我们定义的输入流
    byte[] flush=new byte[1024];
    int len;
    while((len=in.read(flush))!=-1) {
    out.write(flush,0,len);
    }

5.3 字符流

5.3.1 文件字符输入流FileReader

1
2
3
4
5
6
7
8
9
10
11
Reader input=null;
input=new FileReader(file);
//读取操作,分段读取
char[] flush = new char[1024] //缓冲容器
int len=-1;
while((temp=input.read(flush))!=-1) {
//字符数组-->字符串 无解码过程
String str=new String(flush,0,len);
System.out.print(str);
}
input.close();

5.3.2 文件字符输出流FileWriter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Writer output=null;
input=new FileWriter(file);
String msg="";
char[] datas=msg.toCharArray(); //字符串-->字符数组

output.write(datas,0,datas.length);

/*
或者:
output.write(msg); 可以直接传入字符串

或者
output.append(msg); 可以进行多次追加
output.append(msg).append(msg);
*/

output.flush();

input.close();

五、多线程

5.1 基本概念

5.1.1 程序/进程/线程

  • 程序Program
  • 进程Process
  • 线程Thread
  • 并行
  • 并发

5.1.2 线程的分类

  • 守护线程
  • 用户线程

5.1.3 线程的生命周期

5.2 多线程创建方式

5.2.1 方式一:继承Thread类

  • 创建一个继承于Thread类的子类
  • 重写Thread类的run方法,run()为线程执行的主要流程
  • 创建子类对象
  • 通过对象调用start()方法启动线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestThread extends Thread{
private Integer num;
TestThread(Integer num){
this.num = num;
}

@Override
public void run() {
for(int i=0;i<10;i++){
this.num++;
System.out.println(this.num);
}
}

public static void main(String[] args) {
TestThread thread1 = new TestThread(0);
TestThread thread2 = new TestThread(0);
thread1.start();
thread2.start();
}
}

5.2.2 方式二:创建Thread匿名子类

1
2
3
4
5
6
7
8
9
10
new Thread(){
@Override
public void run(){
for(int i=0;i<100;i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}
}.start();
  • Thread类方法
方法 解释
start() 启动线程
run()
getName():String
setName()
static currentThread():Thread 返回当前线程,在Thread子类中就是this,通常用于主线程和Runnable实现类
yield() 线程让步
join() 当前线程阻塞,并调用调用此方法的线程,直到线程执行完毕才切换cpu
sleep(long millis) 单位:毫秒
stop()
isAlive() 判断当前的线程是否处于活动状态,即就绪态和运行态

5.2.3 方式三:实现Runnable接口

  • 创建一个MyRunnable实现接口的Runnable
  • 创建多个Thread线程对象,接收MyRunnable对象作为参数,启动线程调用MyRunnable实现的run方法
  • MyRunnable类的中的数据天然的就可以被多个线程共享,而以第一种方式实现多线程的话,则必须将成员变量定义为静态成员变量才能保证多线程的数据共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MyRunnable implements Runnable {
private int num = 100;

public void run() {
for (int x = 0; x < 100; x++) {
//因为Runnable中没有getName方法,故用Thread中方法获取当前线程对象来实现
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}
}
}


public class Main {

public static void main(String[] args) {
//在这里只需创造一个接口子类对象,后面直接用mr就行了
MyRunnable mr = new MyRunnable();

// Thread t1 = new Thread(mr);
// Thread t2 = new Thread(mr);
// t1.setName("hello");
// t2.setName("java");

//上面这种方法不是很常用,可以用下面这个
Thread t1 = new Thread(mr,"hello");
Thread t2 = new Thread(mr,"java");
t1.start();
t2.start();

}
}

5.2.4 两种方式的选择

  • 开发中优先选择实现Runnable接口的方式
    • 实现接口的方式没有类的单继承的局限
    • 实现接口的方式更适合处理多个线程有共享数据的情况

5.2.5 方式四:实现Callable接口

  • 多线程实现方法:继承Callable接口,重写call方法
  • call方法有返回值,返回值是一个泛型,并且可以抛出异常
  • 我们可以通过futureTask.get()方法获取到线程执行结果,但注意此方法会阻塞主线程执行,知道子线程任务执行完成并返回结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyThread implements Callable {
@override
public Object call() throws Exception {
业务代码
return xxx;
}
}

public static void main(String[] args) {
MyThread mt = new MyThread();
FutureTask futureTask = new FutureTask(mt);
new Thread(futureTask).start();

try {
// 获取call()的返回值
xxx = futureTask.get();
} catch {...}
}

// 匿名内部类方式
FutureTask futureTask=new FutureTask(new Callable() {
@Override
public String call() throws Exception {
Thread.sleep(3000);
System.out.println("calld方法执行了");
return "call方法返回值";
}
});
futureTask.run();
System.out.println("获取返回值: " + futureTask.get());

5.2.6 Future接口

在并发编程中,我们经常用到非阻塞的模型,在之前的多线程的三种实现中,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。

举个例子:比如去吃早点时,点了包子和凉菜,包子需要等3分钟,凉菜只需1分钟,如果是串行的一个执行,在吃上早点的时候需要等待4分钟,但是因为你在等包子的时候,可以同时准备凉菜,所以在准备凉菜的过程中,可以同时准备包子,这样只需要等待3分钟。那Future这种模式就是后面这种执行模式。

5.3 线程优先级

5.3.1 线程等级

  • MAX_PRIORITY:10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5(默认优先级)
  • 以上均为Thread类的常量

5.3.2 方法

  • getPriority():返回线程优先值
  • setPriority(int newPriority):改变线程优先级

5.3.3 说明

  • 线程创建时继承父线程的优先级
  • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

5.4 线程安全

5.4.1 问题

  • 数据被多线程共享时,会存在数据不同步的问题
  • 解决办法:同步代码块

5.4.2 方式一:同步代码块

  • 同步代码块使用synchronized语句,object对象为线程进入同步代码块所需要的锁,这个锁必须是共用的,否则把锁写进run方法体中,线程仍然是不安全的
  • 以上卫生间为例,只用使用了(同步监视器)锁的人把门锁上才能使用卫生间,在此期间,任何人不能使用这间卫生间
  • 将操作共享变量的部分放到同步代码块中即可保证线程安全
  • 缺点:操作同步代码块时,只能有一个线程,无法完成线程切换,相当于单线程,效率不高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MyRunnable implements Runnable{
private int ticket = 100;
final Object object = new Object(); // 锁

@Override
public void run() {
while(true){
// 同步代码块
// 如果是以继承Thread的方式,则锁可以用“类名.class”来使用
synchronized (object){ // 或者锁用this更方便
if(ticket>0){
System.out.println(Thread.currentThread().getName()+": "+ticket);
ticket--;
}
else{
break;
}
}

}
}
}

public class SyncTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();

Thread thread1 = new Thread(myRunnable,"thread-1");
Thread thread2 = new Thread(myRunnable,"thread-2");
Thread thread3 = new Thread(myRunnable,"thread-3");

thread1.start();
thread2.start();
thread3.start();
}
}

5.4.3 方式二:同步方法

  • 如果操作共享数据的代码完整的声明在一个方法中,我们可以将此方法声明为同步的
  • 在方法的返回类型前加上synchronized即可将此方法声明为同步方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MyRunnable implements Runnable{
private int ticket = 100;

@Override
public void run() {
while(true){
show();
}
}

// 同步监视器(锁)默认为this
// 使用继承Thread类的方式,还必须把方法声明为static的才能保证线程安全,它的锁是之前提到的类对象
public synchronized void show(){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+": "+ticket);
ticket--;
}
else{
break;
}
}
}

public class SyncTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();

Thread thread1 = new Thread(myRunnable,"thread-1");
Thread thread2 = new Thread(myRunnable,"thread-2");
Thread thread3 = new Thread(myRunnable,"thread-3");

thread1.start();
thread2.start();
thread3.start();
}
}

5.5 死锁

5.5.1 概念

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

5.5.2 死锁模拟

  • 主线程===>init()===>同步监视器A===>同步监视器B===>last()方法
  • 副线程===>init()===>同步监视器B===>同步监视器A===>last()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class A{
// 同步监视器A
public synchronized void bar(B b){
System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了B实例的bar方法");
try{
Thread.sleep(200);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用B实例的last方法");
b.last();
}

public synchronized void last(){
System.out.println("进入了A类的last方法内部");
}
}

class B{
// 同步监视器B
public synchronized void bar(A a){
System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入了B实例的bar方法");
try{
Thread.sleep(200);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用A实例的last方法");
a.last();
}

public synchronized void last(){
System.out.println("进入了B类的last方法内部");
}
}

public class DeadLock implements Runnable{
A a = new A();
B b = new B();

public void init(){
Thread.currentThread().setName("主线程");
a.foo(b);
System.out.println("进入主线程之后");
}

public void run(){
Thread.currentThread().setName("副线程");
b.bar(a);
System.out.println("进入副线程之后");
}

public static void main(String[] args){
DeadLock dl = new DeadLock();
new Thread(dl).start(); // 副线程
dl.init(); // 主线程
}
}

5.5.3 Lock锁

  • Lock是jdk5.0新增的特性

  • synchronized和lock的异同

    • 相同:二者都可以解决线程安全问题
    • 不同:synchronized机制在执行完相应的同步代码之后,自动的释放同步监视器,Lock需要手动的启动同步(lock()),同时结束同步也需要手动实现(unlock())
  • 优先使用顺序:Lock->同步代码块(已经进入方法体,分配了相应资源)->同步方法(在方法体之外)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MyRunnable implements Runnable{
private int ticket = 100;

// 1. 生成lock对象
private ReentrantLock lock = new ReentrantLock(true);

@Override
public void run() {
while(true){
try{
// 2. 调用锁定方法lock()
lock.lock();

if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName()+": "+ticket);
ticket--;
}
else{
break;
}
}finally{
// 调用解锁方法unlock()
lock.unlock();
}

}
}
}

public class SyncTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();

Thread thread1 = new Thread(myRunnable,"thread-1");
Thread thread2 = new Thread(myRunnable,"thread-2");
Thread thread3 = new Thread(myRunnable,"thread-3");

thread1.start();
thread2.start();
thread3.start();
}
}

5.6 线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程对性能影响很大
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用;类似于共享单车
  • 好处:提高相应速度(减少了创建新线程的时间);降低资源消耗;便于线程管理:corePoolSize(核心池的大小)、maximumPoolSize(最大线程数)、keepAliveTime(线程没有任务时最长的保持时间)
  • JDK5提供了线程池API:ExecutorService和Executors
  • ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
    • void execute(Runnable command):执行任务/命令,一般用来执行Runnable
    • <T> Future <T> submit(Callable<T> task):执行任务,有返回值,一般用来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
    • Executors.newCacheThreadPool():创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程
    • Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池,可控制最大并发数,超出的线程进行队列等待。
    • Executors.newSingleThreadExecutor():创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyThread implements Runnable {
private int num = 100;

public void run() {
for (int x = 0; x < 100; x++) {
//因为Runnable中没有getName方法,故用Thread中方法获取当前线程对象来实现
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}
}
}

public class ThreadPool1{
public static void main(String[] args){
ExecutorService service = Executors.newFixedThreadPool(2);
ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
service1.corePoolSize(15);

service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}

一、诗体简介

1.1 近体诗格律

古体诗与近体诗的区别:

  • 古体诗不讲究押韵;近体诗用韵严格,必须一韵到底,不能换韵,且压平声韵

  • 古体诗字数不固定;近体诗字数固定

  • 古体诗没有严格的平仄、对仗;近体诗讲究平仄、对仗工整

古体诗的代表有《诗经》《楚辞》《琵琶行》《归园田居》等

近体诗格律要求:

  • 诗句数固定
  • 字数为五言或七言
  • 一般押平声韵,不允许换韵,韵脚位置固定
  • 平仄有规定
  • 五七言律诗中间两联原则上必须对仗

1.2 绝句和律诗

  • 绝句为四句;律诗为八句
  • 古绝讲究押韵,平仄与对仗较为自由;律诗即讲究押韵,又对平仄、对仗有严格规定
  • 绝句(截句、绝诗)包括古绝和律绝两种,短小精炼

二、声律

2.1 声调

  • 古汉语四声调:平、上、去、入
  • 相间律:平仄交替使用
古声调 普通话声调
阴平、阳平
去(第三声)
去(第四声)
融合到了平/上/去

由于现代汉语没有了入声的发音习惯,对古代入声字的判断规则如下:

(一)凡b、d、g、j、zh、z六母的第二声字(阳平),都是古入声字

(二)凡d、t、L、z、c、s等六母跟韵母e拼合时,不论国语读何声调,都是古入声字

(三)凡k、zh、ch、sh、R五母与韵母uo拼合时,不论国语读何声调,都是古入声字

(四)凡b、p、m、d、t、n、L七母跟韵母ie拼时,无论国语读何声调,都是古入声字

(五)凡d、g、h、z、s五母与韵母ei拼合时,不论国语读何声调,都是古入声字

(六)凡声母f,跟韵母a、o拼合时,都是古入声字

(七)凡读ue韵母的字,都是古入声字。只有「嗟」jue,「瘸」queˊ,「靴」xue三字除外

(八)一字有两读,读音为开尾韵,语音读i或u韵尾的,也是古入声字

2.2 五言律诗的平仄

2.2.1 起声/收声

  • 仄起:第一句的第二个字为仄声
  • 平起:第一句的第二个字为平声
  • 仄收:第一句的第五字为仄声
  • 平收:第一句的第五字为平声

不同平仄格式的首句格式

  • 仄起仄收:仄仄平平仄、仄仄平平平仄仄
  • 仄起平收(首句入韵):仄仄仄平平、仄仄平平仄仄平
  • 平起平收(首句入韵):平平仄仄平、平平仄仄仄平平
  • 平起仄收:平平平仄仄、平平仄仄平平仄

2.2.2 平仄格式

(1)仄起仄收

加下划线表示可平可仄

$\underline{仄}仄平平仄,平平仄仄平$

$\underline{平}平仄仄平,\underline{仄}仄仄平平$

$\underline{仄}仄平平仄,平平仄仄平$

$\underline{平}平仄仄平,\underline{仄}仄仄平平$

  • 堂:泛指房屋的正厅

  • 无生:佛家语,谓世本虚幻,万物实体无生无灭

(2)仄起平收

$\underline{仄}仄仄平平,平平仄仄平$

$\underline{平}平平仄仄,\underline{仄}仄仄平平$

$\underline{仄}仄平平仄,平平仄仄平$

$\underline{平}平平仄仄,\underline{仄}仄仄平平$

终南山:在长安南五十里,秦岭主峰之一。古人又称秦岭山脉为终南山。秦岭绵延八百余里,是渭水和汉水的分水岭。

太乙:又名太一,秦岭之一峰。唐人每称终南山一名太一,如《元和郡县志》:”终南山在县(京兆万年县)南五十里。按经传所说,终南山一名太一,亦名中南”。

天都:天帝所居。

青霭:山中的岚气。霭:云气。

海隅:海边。终南山并不到海,此为夸张之词。

分野:古天文学名词。古人以天上的二十八个星宿的位置来区分中国境内的地域,被称为分野。

壑:山谷。

(3)平起仄收

$\underline{平}平平仄仄,仄仄仄平平$

$\underline{仄}仄平平仄,\underline{平}平仄仄平$

$\underline{平}平平仄仄,\underline{仄}仄仄平平$

$\underline{仄}仄平平仄,\underline{平}平仄仄平$

(4)平起平收

$\underline{平}平仄仄平,仄仄仄平平$

$\underline{仄}仄平平仄,\underline{平}平仄仄平$

$\underline{平}平平仄仄,\underline{仄}仄仄平平$

$\underline{仄}仄平平仄,\underline{平}平仄仄平$

代州:治所在今山西代县

刁斗:古代行军用具。斗形有柄,铜质;白天用作炊具,晚上击以巡更

三军:军队的通称

井陉$x\acute{i}ng$:古关名,即井陉口,又名井陉关,唐时要塞,在今河北井陉县境内,井隘北井隆山上。秦汉时为军事要地。

凤阙:汉代宫阙名,在建章宫东,因为其上有铜凤凰而得名

取龙庭:借指誓歼敌虏。龙庭:原指匈奴单于祭天的地方。

2.3 七言律诗的平仄

2.3.1 平仄格式

(1)平起平收

$\underline{平}平\underline{仄}仄仄平平,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

(2)平起仄收

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

(3)仄起平收

$\underline{仄}仄平平仄仄平,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

(4)仄起仄收

$仄仄平平平仄仄,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

$\underline{仄}仄\underline{平}平平仄仄,\underline{平}平\underline{仄}仄仄平平$

$\underline{平}平\underline{仄}仄平平仄,\underline{仄}仄平平仄仄平$

2.4 相对律、相粘律

2.4.1 相对律

  • 一联中,出句对句的平仄是相对的

2.4.2 相粘律

  • 后一联出句第二字的平仄要跟前一联对句第二字的平仄一致
  • 即:第三句第二字的平仄与第二句第二字的平仄相同,第五局第二字的平仄和第四句第二字的平仄相同,第七句第二字的平仄与第六句第二字的平仄相同

2.5 特殊平仄格式

在唐人近体诗创作中存在对句型为【平平平仄仄】、【仄仄平平平仄仄】平仄格式进行修改的情况,如下:

  • 五言:平平【平仄仄】—->平平仄【平仄】
  • 七言:仄仄平平【平仄仄】—->平仄平平【仄平仄】

句型修改为特殊平仄格式后,这种句型的第一个字必须为平声

2.6 一三五不论、二四六分明

这句话的意思是:

  • 近体七言诗每句的第一字、第三字和第五字的平仄可以不论
  • 第二字、第四字和第六字的平仄必须严格依照格式

当然这句话不一定要完全遵循,有例外情况

2.6.1 反例

以下反例都是“一三五不论、二四六分明”所导致的情况,不符合诗词的韵律

  • 犯孤平:诗的一句中,除了韵脚外只有一个平字,这是作诗的禁忌
  • 三平调:句末三字的平仄为连着的三平声

2.7 拗与救

  • 拗:不符合平仄格式的字,这种形式叫做拗
  • 救:如果前一个字用了“拗”,比如前一个字在本该用平声的地方用了仄声,那么在后面合适的地方补充一个平声

三、用韵

3.1 韵母解析

押韵:押韵是把同韵母或韵母相近的字,放在诗篇某些句子的末尾,使诗歌读起来顺口,听起来悦耳,容易记得住、传得开

韵母由韵头、韵腹、韵尾三个部分组成

  • 韵头:又称介音,是韵腹前面、起前导作用的部分,发音比较模糊,往往迅速带过;
  • 韵腹:又称主要元音,是一个韵母发音的关键,是韵母发音过程时,口腔肌肉最紧张,发音最响亮的部分;
  • 韵尾:可元音可辅音。一种叫鼻韵尾,有-n,-ng两个。另一种叫口韵尾。韵尾是韵腹后面、起收尾作用的部分,发音也比较模糊,但务求发到位。

如“娘”niáng的韵母是iang,其中i是韵头,a是韵腹,ng是韵尾。

每个韵母一定有韵腹,韵头和韵尾则可有可无。如“大”dà的韵母是a,a是韵腹,没有韵头、韵尾;
“瓜”guā的韵母是ua,其中u是韵头,a是韵腹,没有韵尾;
“刀”dāo的韵母是ao,其中a是韵腹,o是韵尾,没有韵头。

3.2 平声韵

平声韵,是汉语中读音为平声的单字归类,其主要特点是发声比较平和。“平声”与“仄声”相对应。在诗词中,句尾押韵有压平声韵和仄声韵, 平声即阴平、阳平,仄声是上声、去声。压阴平、阳平韵的是平声韵,压上声、去声的是仄声韵。

做诗所依据的韵书,一般指《平水韵》,平、上、去、入四声一共106韵

平声30韵,上声29韵,去声30韵,入声17韵。律诗一般只用平声韵

  • 上平声:一东、二冬、三江、四支、五微、六鱼、七虞、八齐、九佳、十灰、十一真、十二文、十三元、十四寒、十五删
  • 下平声:一先、二萧、三肴、四豪、五歌、六麻、七阳、八庚、九青、十蒸、十一尤、十二侵、十三覃、十四盐、十五咸
  • 上声韵:一董、二肿、三讲、四纸、五尾、六语、七雨、八荠(jì)、九蟹、十贿、十一轸(zhěn)、十二吻、十三阮、十四旱、十五潸(shān)、十六铣(xiǎn/xǐ)、十七小、十八巧、十九皓、二十哿、二十一马、二十二养、二十三梗、二十四迥(jiǒng)、二十五有、二十六寝、二十七感、二十八琰(yǎn)、二十九豏(xiàn)
  • 去声韵:一送、二宋、三绛(jiàng)、四寘(zhì)、五未、六御、七遇、八霁(jì)、九泰、十卦、十一队、十二震、十三问、十四愿、十五翰(hàn)、十六谏、十七霰(xiàn)、十八啸、十九效、二十号、二十一箇(gè)、二十二禡(mà)、二十三漾、二十四敬、二十五径(jìng)、二十六宥(yòu)、二十七沁、二十八勘(kān)、二十九艳、三十陷
  • 入声韵:一屋、二沃、三觉、四质、五物、六月、七曷(hé)、八黠(xiá)、九屑(xiè)、十药、十一陌、十二锡(xī)、十三职、十四缉(qī/jī)、十五合、十六叶、十七洽(qià)

3.3 宽韵与窄韵

宽韵:包括四支、一先、七阳、八庚、十一尤、一东、十一真、七虞.作诗用这些韵,有较多的韵脚可供选择

中韵:包括十三元、十四寒、六鱼、二萧、十二侵、二冬、十灰、八齐、五歌、六麻、四豪.作诗用这些韵,有次多的韵脚可供选择

窄韵:包括五微、十二文、十五删、九青、十蒸、十三覃、十四盐.作诗用这些韵,可供选择的较少

险韵:包括包括三江、九佳、三肴、十五咸.作诗用这些韵,可供选择的很少

四、对仗

4.1 对仗要求

  • 字面相对:构成对仗的字词意义相对或相反
  • 词性一致:名词对名词,动词对动词,形容词对形容词等
  • 对仗时,对应位置上的字不能相同,如昔我往矣,杨柳依依;今我来思,雨雪霏霏

4.2 对仗的类型

  • 言对:
  • 事对:
  • 反对:
  • 正对:

一、浮动

1.1 概念

  • 通过浮动可以使一个元素向其父元素的左侧或右侧移动
  • 使用 float 属性来设置于元素的浮动,可选值:
    • none 默认值 元素不浮动
    • left 元素向右浮动
    • right 元素向右浮动

1.2 特点

  1. 浮动的元素会完全脱离文档流,不再占据文档流中的位置
  2. 设置浮动以后元素回向父元素的左侧或右侧移动
  3. 浮动元素默认不会从父元素中移出
  4. 浮动元素向左或向右移动时,不会超过它前边的其他浮动元素
  5. 如果浮动元素的上边是一个没有浮动的块元素,则浮动元素无法上移
  6. 浮动元素不会超过它上边的浮动的兄弟元素,最多就是和它一样高

1.3 注意

  1. 元素设置浮动以后,水平布局的等式便不需要强制成立
  2. 元素设置浮动以后,会完全从文档流中脱离,不在占用文档流的位置,所以元素下边的还在文档流中的元素会自动向上移动

1.4 总结

  • 浮动目前来讲它的主要作用就是让页面中的元素可以水平排列
  • 通过浮动可以制作一些水平方向的布局
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    .box1{
    width: 200px;
    height: 200px;
    background-color: #bfa;

    float: left;
    }

    .box2{
    width: 200px;
    height: 200px;
    background-color: orange;
    float: left;
    }

    .box3{
    width: 200px;
    height: 200px;
    background-color: yellow;
    float: left;
    }
    </style>
    </head>
    <body>
    <div class="box1"></div>
    <div class="box2"></div>
    <div class="box3"></div>
    </body>
    </html>

1.5 浮动与文字

  • 浮动元素不会盖住文字,文字会自动环绕在浮动元素的周围,所以我们可以利用浮动来设置文字环绕图片的效果

1.6 脱离文档流

1.6.1 概念

  • 元素设置浮动以后,将会从文档流中脱离后,元素的一些特点也会发生变化

1.6.2 特点

(1)块元素

  1. 块元素不再独占页面的一行
  2. 脱离文档流以后,块元素的宽度和高度默认都被内容撑开

(2)行内元素

  • 行内元素脱离文档流以后变成块元素,特点和块元素一样
  • 脱离文档流以后,不需要再区分块和行内了

二、网页布局

2.1 结构

  • 头部
  • 主体
  • 左侧导航
  • 中间的内容
  • 右边的边栏
  • 网页的底部

2.2 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
header, main, footer{
width: 1000px;
margin: 0 auto;
}

/* 设置头部 */
header{
height: 150px;
background-color: silver;
}

/* 设置主体 */
main{
height: 500px;
background-color: #bfa;
margin: 10px auto;
}

nav, article, footer{
float: left;
height: 100%;
}

/* 设置左侧的导航 */
nav{
width: 190px;
background-color: yellow;
}

/* 设置中间的内容 */
article{
width: 600px;
background-color: orange;
margin: 0 10px;
}

/* 设置右侧的内容 */
aside{
width: 190px;
background-color: pink;
}

/* 设置底部 */
footer{
height: 150px;
background-color: tomato;
}
</style>
</head>
<body>
<!-- 创建头部 -->
<header></header>

<!-- 创建网页的主体 -->
<main>
<!-- 左侧导航 -->
<nav></nav>

<!-- 中间的内容 -->
<article></article>

<!-- 右边的边栏 -->
<aside></aside>

</main>

<!-- 网页的底部 -->
<footer></footer>
</body>
</html>

三、BFC

3.1 概念

  • BFC(Block Formatting Context) 块级格式化环境
  • BFC是CSS中的一个隐含的属性,可以为一个元素开启BFC
  • 开启BFC,该元素会变成一个独立的布局区域

3.2 特点

  1. 开启BFC的元素不会被浮动元素所覆盖
  2. 开启BFC的元素子元素和父元素外边距不会重叠
  3. 开启BFC的元素怒可以包含浮动的子元素

3.3 BFC开启方式

  1. 设置元素的浮动(不推荐)
  2. 将元素设置为行内块元素(不推荐)
  3. 将元素的overflow设置为一个非visible的值
  4. 常用的方式 为元素设置overflow:hidden 开启其BFC,以使其可以包含浮动元素

3.4 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box1{
width: 200px;
height: 200px;
background-color: #bfa;
/* float: left; */
overflow: hidden;
}

.box2{
width: 200px;
height: 200px;
background-color: orange;
overflow: hidden;
}

.box3{
width: 100px;
height: 200px;
background-color: yellow;
margin-top: 100px;
}
</style>
</head>
<body>
<div class="box1">
<div class="box3"></div>
</div>



<!-- <div class="box2">

</div> -->

</body>
</html>

四、高度塌陷

4.1 概念

  • 在浮动布局中,父元素的高度默认被子元素撑开
  • 当子元素浮动后,其会完全脱离文档流,子元素从文档流中脱离
  • 将会无法撑起父元素的高度,导致父元素的高度丢失
  • 父元素高度丢失以后,其下的元素会自动上移,导致页面布局混乱
  • 所以高度塌陷使浮动布局中比较常见的问题,这个问题必须要进行处理

4.2 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer{
border: red solid 10px;
/* 开启BFC */
/* float: left; */
/* display: inline-block; */
overflow: hidden;
}

.inner{
width: 100px;
height: 100px;
background-color: #bfa;

/* 高度塌陷的问题 */
float: left;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
</div>
</body>
</html>

4.3 解决方法

4.3.1 方法一:clear

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
font-size: 50px;
}
.box1{
width: 200px;
width: 200px;
background-color: #bfa;
float: left;
}

.box2{
width: 400px;
width: 400px;
background-color: #bfa;
float: right;
}
.box3{
width: 200px;
height: 200px;
background-color: orange;
/*
由于box1的浮动,导致box3位置上移
也就是box3收到了box1浮动的影响,位置发生了变化

如果我们不希望某个元素因为其他元素浮动的影响而改变位置,
可以通过clear属性来清楚浮动元素对当前元素所产生的影响

clear
作用:清除浮动元素对当前元素所产生的影响
可选值
left:清除左侧浮动元素对当前元素的影响
right:清除左侧浮动元素对当前元素的影响
both:清除两侧影响最大的那侧

原理:
设置清除浮动以后,浏览器会自动为元素添加一个上外边距,
以使其位置不受其他元素的影响

*/
}
</style>
</head>
<body>
<div class="box1">1</div>
<div class="box2"></div>
<div class="box3">3</div>
</body>
</html>

4.3.2 方法二:伪类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* 解决外边距重叠,父元素会继承第一个子元素的外边距 */
.box1{
width: 200px;
height: 200px;
background-color: #bfa;
}

/* .box1::before{
content: '';
display: table;
} */

.box2{
width: 100px;
height: 100px;
background-color: orange;
margin-top: 100px;
}

/* clearfix可以同时解决高度塌陷和外边距重叠的问题,
当遇到这些问题时,直接使用clearfix这个类即可 */
.clearfix::before,
.clearfix::after{
content: '';
display: table;
clear: both;
}
</style>
</head>
<body>
<div class="box1 clearfix">
<div class="box2"></div>
</div>
</body>
</html>

五、定位

5.1 概念

  • 定位是一种更加高级的布局手段
  • 通过定位可以将元素摆放到页面的任意位置
  • 使用position来设置定位
    • static 默认值,元素是静止的,没有开启定位
    • ralative 开启元素的相对定位
    • absolute 开启元素的绝对定位
    • fixed 开启元素的固定定位
    • sticky 开启元素的粘滞定位

5.2 相对定位

5.2.1 概念

  • 当元素的position属性值设置为relative时,则开启了元素的相对定位

5.2.2 特点

  1. 元素开启相对定位以后,如果不设置偏移量,元素不会发生任何的变化
  2. 相对定位是参照于元素在文档流中的位置进行定位
  3. 相对定位会提升元素的层级
  4. 相对定位不会使元素脱离文档流
  5. 相对定位不会改变元素的性质,块还是块,行内还是行内

5.3 偏移量

  • 当元素开启了定位以后,可以通过偏移量来设置元素的位置
  • top:定位元素和定位位置上边的距离
  • bottom:定位元素和定位位置下边的距离
    • 定位元素垂直方向的位置由top和bottom两个属性来控制,通常情况下只会使用其中之一
    • top值越大,定位元素越向下移动
    • bottom值越大,定位元素越向上移动
  • left:定位元素和定位位置左边的距离
  • right:定位元素和定位位置右边的距离
    • 定位元素水平方向的位置由left和right两个属性来控制,通常情况下只会使用其中之一
    • left值越大,定位元素越向右移动
    • right值越大,定位元素越向左移动

5.4 绝对定位

5.4.1 概念

  • 当元素的position属性值设置威absolute时,则开启了元素的绝对定位

5.4.2 特点

  1. 开启绝对定位后,如果不设置偏移量,元素的位置不会发生变化
  2. 开启绝对定位后,元素会从文档流中脱离
  3. 绝对定位会改变元素的性质,行内变成块,块的宽高被内容撑开
  4. 绝对定位会使元素提升一个层级
  5. 绝对定位元素是相对于其包含块进行定位的

5.4.3 绝对定位元素的布局

  • 水平布局:
    • left + margin-left + border-left + padding-left + width + padding-right + border-right + margin-right + right= 父元素内容区宽度
  • 当我们开启了绝对定位后,水平方向的布局等式就需要添加 left 和 right 两个值
  • 此时规则和之前一样,只是多添加了两个值
    • 当发生过渡约束,
    • 如果9个值中没有auto,则自动调整right值,以使等式成立
    • 如果由auto,则自动调整auto值,以使等式成立
  • 可设置auto的值
    • margin width left right
    • 因为left 和 right的值默认是auto,所以如果不知道left和right,则等式满足时,会自动调整这两个值
  • 垂直方向布局的等式也必须满足:
    • top + margin-top/bottom + padding-top/bottom +border-top/bottom + bottom = 包含块的高度

5.5 包含块(containing block)

  • 正常情况下,包含块就是离当前元素最近的祖先块元素
    1
    2
    <div><div></div></div>
    <div><span><em></em></span></div>
  • 绝对定位的包含块
    • 包含快就是离它最近的开启了定位的祖先元素
    • 如果所有的祖先元素都没有开启定位,则相对于根元素进行定位:html(根元素、初始包含块)

5.6 固定定位

5.6.1 概念

  • 将元素的position属性设置为fixed,则开启了元素的固定定位
  • 固定定位也是一种绝对定位,所以固定定位的大部分特点都和绝对定位一样
    • 唯一不同的是固定定位永远参照于浏览器的视口进行定位
    • 固定定位的元素不会随网页的滚动条滚动

5.7 粘滞定位

5.7 1 概念

  • 当元素的position属性设置为sticky时,则开启了元素的粘滞定位
  • 粘滞定位和相对定位的特点基本一致
    • 不同的是粘滞定位可以在元素到达某个位置时将其固定

5.8 元素的层级

  • 对于开启了定位元素,可以通过z-index属性来指定元素的层级
  • z-index需要一个整数作为参数,值越大的元素层级越高,元素的层级越高越优先显示
  • 如果元素层级一样,优先显示靠下的元素
  • 祖先元素的层级再高也不会盖住后代元素

一、HTML基础

1.1 基础网页结构

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>day01</title>
</head>
<body>

</body>
</html>

1.1.1 结构

  • 头部:<header>
  • 主体:<main>
  • 底部:<footer>

1.1.2 内容区

  • 导航:<nav>
  • 侧边栏:<aside>
  • 文章区:<artical>
  • 读取区块:<section>

1.1.3 注意

  • 1.1.1和1.1.2的标签是h5新增的标签,使用的不多,一般使用div和span
  • div没有语义,就用来表示一个区域,目前来讲div还是我们主要的布局元素
  • span是一个行内元素,没有任何的语义,一般用于在网页中选中文字

1.2 实体符号

实体符 含义
\&nbsp 表示空格
\&gt 大于号
&lt 小于号
&copy 版权符号

1.3 块元素与行内元素

1.3.1 块元素(block element)

  • 在页面中独占一行的元素
  • 块元素基本上什么都能放

1.3.2 行内元素(inline element)

  • 在页面中不会独占一行的元素,且该元素的宽高由其内容撑开
  • 行内元素主要用来包裹文字
  • 一般情况下会在块元素中放行内元素,而不会在行内元素中放块元素
  • 浏览器对网页进行解析时,会自动对网页中不符合规范的内容进行修正

二、CSS

2.1 网页组成部分

  • 结构(HTML)
  • 表现(CSS)
  • 行为(JS)

2.2 CSS介绍

  • 网页实际上是一个多层的结构,通过CSS可以分别为网页的每一层来设置样式
  • 而最终我们能看到的知识网页的最上边一层
  • 总之一句话,CSS用来设置网页中元素的样式

2.3 使用方式

2.3.1 第一种:内联样式,行内样式

1
2
3
4
5
6
7
<!--在标签内部通过style属性来设置元素的样式
问题:
1. 使用内联样式,样式只能对一个标签生效
2. 如果希望影响到多个元素必须在每个元素中都复制一遍
3. 并且当样式发生变化时,我们必须要一个一个的修改,非常的不方便
注意:开发时绝对不要使用内联样式-->
<p style="color: red;font-size: 20px;">少小离家老大回,乡音无改鬓毛衰</p>

2.3.2 第二种:内部样式表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 
第二种方式(内部样式表)
将样式编写到head钟的style标签里
然后通过CSS的选择器来选中元素并为其设置各种样式
可以同时为多个标签设置样式,并且修改时只需要修改一处即可全部应用
内部样式表更加方便对样式进行复用
问题:
我们的内部样式表只能对一个网页起作用
它里面的样式不能跨页面进行复用
-->
<style>
p{
color: aqua;
font-size: 50px;
}
</style>

2.3.3 第三种:外部样式表(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 
第三种方式(外部样式表)推荐方式
可以将CSS样式编写到一个外部的CSS文件中
然后通过link标签来引入外部的CSS文件
外部样式表需要通过link标签进行引入
意味着只要想使用这些样式的网页都可以对其进行引用
使样式可以在不同页面之间进行复用
将样式编写到外部的CSS文件中,可以最大限度的使用到浏览器的缓存机制
从而加快网页的加载速度,提高用户的体验

-->

<link rel="stylesheet" href="./style.css">

2.4 基本语法

1
2
3
4
5
选择器{
样式:样式值;
样式:样式值;
...
}

2.5 样式的继承

2.5.1 概念

  • 样式的继承:我们为一个元素设置的样式同时也会应用到它的后代元素上
  • 继承是发生在祖先和后代之间的

2.5.2 作用

  • 继承的设计是为了方便我们的开发,
  • 利用继承我们可以将一些通用的样式统一设置到共同的祖先元素上,
  • 这样只需要设置一次即可让所有的元素都具有该样式

2.5.3 注意

  • 注意:并不是所有的样式都会被继承
  • 比如 背景相关的,布局相关等的这些样式都不会被继承
1
2
3
4
5
6
7
8
9
10
11
12
<style>
/*p元素设置的颜色也会作用到子元素span上*/
p{
color: red;
}
</style>

<p>
我是一个p元素
<span>我是p元素中的span</span>
</p>
<span>我是p元素外的span</span>

三、CSS选择器

3.1 基本选择器

  • 元素选择器:标签名{}
  • id选择器:#id值{}
  • 类选择器:.类名{}
  • 通配选择器:*{}

3.2 复合选择器

3.2.1 交集选择器

  • 作用:选中同时符合多个条件的元素
  • 语法:选择器1 选择器2 … 选择器n{}
  • 注意点:
    • 交集选择器中如果有元素选择器, 必须使用元素选择器开头
1
2
3
4
5
6
7
8
9
10
11
12
<style>
/* 要求带有class为red的div */
div.red{
color: red;
font-size: 30px;
}

/* 要求同时具有a b c三个类 */
.a.b.c{
color: aqua;
}
</style>

3.2.2 并集选择器

  • 作用:同时选择多个选择器对应的元素
  • 语法:选择器1,选择器2,…,选择器n{}
    1
    2
    3
    4
    5
    <style>
    h1, span{
    color: blueviolet;
    }
    </style>

3.3 关系选择器

3.3.1 元素关系

  • 父元素:直接包含子元素的元素叫父元素
  • 子元素:直接被父元素包含的元素是子元素
  • 祖先元素:直接或简介包含后代元素的元素叫做祖先元素
    • 一个元素的父元素也是它的祖先元素
  • 后代元素:直接或间接被祖先元素包含的元素
    • 子元素也是后代元素
  • 兄弟元素:拥有相同父元素的元素是兄弟元素

3.3.2 子元素选择器

  • 作用:选中指定父元素的指定子元素
  • 语法:父元素>子元素
    1
    2
    3
    4
    5
    <style>
    div > span{
    color: red;
    }
    </style>

3.3.3 后代选择器

  • 作用:选中指定元素内的指定后代元素
  • 语法:祖先 后代
    1
    2
    3
    4
    5
    <style>
    div span{
    color: aqua;
    }
    </style>

3.3.4 兄弟选择器:选择下一个兄弟

  • 语法:前一个 + 下一个
  • 只对下一个兄弟起作用,其他兄弟不起作用
    1
    2
    3
    4
    5
    <style>
    p + span{
    color:red;
    }
    </style>

3.3.5 兄弟选择器:选择下面所有兄弟

  • 语法:前一个 ~ 下一个
  • 只对下面所有的兄弟起作用,前面的兄弟不起作用
    1
    2
    3
    4
    5
    <style>
    p ~ span{
    color:red;
    }
    </style>

3.4 属性选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
/*[属性名] 选择含有指定属性的元素*/

/*[属性名=属性值] 选择含有指定属性和属性值的元素*/
p[title=abc]{
color: orange;
}

/*[属性名^=属性值] 选择属性值以指定值开头的元素*/
p[title^=abc]{
color: orange;
}

/*[属性名$=属性值] 选择属性值以指定值结尾的元素*/
p[title$=abc]{
color: orange;
}

/*[属性名*=属性值] 选择属性值中含有某值的元素*/
p[title*=e]{
color: orange;
}

<!-- title表示鼠标悬停时显示的文字 -->
<p title="abc">少小离家老大回</p>
<p title="abcdef">乡音无改鬓毛衰</p>
<p title="helloabc">乡音无改鬓毛衰</p>
<p title="hello">儿童相见不相识</p>
<p>秋水共长天一色</p>
<p> 落霞与孤鹜齐飞</p>
</style>

3.5 伪类选择器

3.5.1 概念

  • 伪类(不存在的类,特殊的类)
  • 伪类用来描述一个元素的特殊状态
  • 比如:第一个子元素,被点击的元素、鼠标移入的元素

3.5.2 常用的伪类选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:first-child    第一个子元素
:last-child 最后一个子元素
:nth-child() 选中第n个子元素
特殊值:
n 第n个 n的范围0到正无穷
2n 或 even 选中偶数位的元素
2n+1 或 odd 都表示奇数位元素
以上这些伪类都是根据所有子元素进行排序

:first-of-type
:last-of-type
:nth-of-type()
这几个伪类的功能和上述的类似,不同点是他们是在同类型元素中进行排序

:not() 否定伪类
将符合条件的元素从选择器中去除

3.5.3 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<style>
ul>li:first-child{
color: red;
}

ul>li:last-child{
color: red;
}

/* 选中所有元素*/
ul>li:nth-child(n){
color: red;
}

/* 选中偶数位元素元素*/
ul>li:nth-child(2n){
color: red;
}

/* 如果要选择li中的第三个需要用:nth-of-type(3) */
ul > li:not(:nth-child(3)){
color: orange;
}
</style>

<ul>
<!--
<span>我是span</span>
加上这句话,first-child就不能选中第一个li,
可以使用first-of-type
-->
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
<li>第四个</li>
<li>第五个</li>
</ul>

3.5.4 a元素的伪类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<style>
/*
:link 用来表示没访问过的链接(正常的链接)
*/
a:link{
color: red;
}

/*
:visited 用来表示访问过的链接

由于隐私的原因,所以visited这个伪类只能修改链接的颜色
*/

a:visited{
color: orange;
}

/*
:hover 用来表示鼠标移入的状态
*/

a:hover{
font-size: 30px;
color: aqua;
}

/*
:active 用来表示鼠标点击的状态
*/

a:active{
color: yellowgreen;
}

/* hover和active 别的标签也能使用 */
</style>
<!--
1. 没有访问过的链接
2. 访问过的链接
-->
<a href="https://www.baidu.com">Baidu</a>

<br/>

<a href="https://www.baidu.com"></a>

3.6 伪元素选择器

3.6.1 概念

  • 表示页面中一些并不真实的存在的元素(特殊的位置)
  • 位元素使用 :: 开头
  • ::first-letter 表示第一个字母
  • ::first-line 表示第一行
  • ::selection 表示鼠标拖动选中的内容
  • ::before 表示元素的开始位置
  • ::after 表示元素结尾的位置
    • before和after必须结合content属性来使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<style>
p::first-letter{
font-size: 30px;
}

p::first-line{
background-color: yellow;
}

p::selection{
background-color: greenyellow;
}

div::before{
content: 'abc';
color: red;
}

div::after{
content: '123';
color: blue;
}
</style>
<div>Hello How are you</div>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Consequuntur necessitatibus eaque cupiditate! Eum fuga eos nam placeat magnam libero quia suscipit? Provident natus iste perspiciatis voluptas animi impedit sint? Eius.
</p>

3.7 选择器的权重

3.7.1 样式的冲突

  • 当我们通过不同的选择器,选中一个相同的元素,并且为相同的样式设置不同的值,此时就发生了样式的冲突
  • 发生样式冲突时,应用哪个样式由选择器的优先级(权重)决定

3.7.2 权重

样式 权重
内联样式 1,0,0,0
id选择器 0,1,0,0
类和伪类选择器 0,0,1,0
元素选择器 0,0,0,1
通配选择器 0,0,0,0
继承的样式 没有优先级

3.7.3 注意

  • 比较优先级时,需要将所有的选择器的优先级进行相加计算,最后优先级越高,则越优先显示
  • 分组选择器是单独计算
    • 选择器的累加不会超过其最大的数量级,类选择器再高也不会超过id选择器
    • 如果优先级计算后相同,则优先使用靠下的样式
  • 可以再某一个样式的后边添加!important,则此时该样式获取到最高的优先级,甚至超过内联样式
    • 注意:在开发中一定要慎用
1
2
3
4
5
6
7
8
9
10
11
12
<style>
*{
font-size: 50px;
}

div{
font-size: 20px;
}
</style>

<!-- 因为span是继承div的,没有优先级,所有span使用的是通配中的样式 -->
<div id="box" class="red" style="color: blue;">我是一个div <span>我是div中的span</span></div>

四、CSS单位与颜色

4.1 长度单位

4.1.1 像素与百分比

  • 像素:同样的200px在不同的设备下显示效果不一样
  • 百分比:也可以将属性值设置为相对于其父元素属性的百分比
    ;可以使子元素跟随父元素的改变而改变
1
2
3
4
5
6
7
8
9
10
11
12
<style>
.box1{
width: 200px;
height: 200px;
background-color: orange;
}
.box2{
width: 50%;
height: 50%;
background-color: aqua;
}
</style>

4.1.2 em和rem

  • em:em是相对于元素的字体大小来计算的;
    • 1em=1font-size
    • em会根据字体大小的改变而改变
  • rem:rem是相对于根元素的字体大小来计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.box1{
font-size: 10px;
width: 10em;
height: 10em;
background-color: yellow;
}

.box1{
width: 10rem;
height: 10rem;
background-color: yellow;
}
</style>

4.1.3 颜色单位

  • RGB值:RGB通过三种颜色的不同浓度来调配出不同的颜色
    • 语法:rgb(红色,绿色,蓝色)
  • RGBA:A表示透明度
    • 值为1表示不透明
    • 值为0表示透明
    • 值为.5表示半透明
  • 十六进制RGB值:
    • 语法:#红色绿色蓝色
    • 如:#FFFFFF 表示白色
  • HSL/HSLA值:
    • H:色相 (0 - 360)
    • S:饱和度(0 - 100%)
    • L:亮度(0 - 100%)

五、CSS盒子模型

5.1 文档流(normal flow)

网页是一个多层的结构
通过css可以分别为每一层来设置样式
作为用户只能看到最顶上一层
这些层中,最底下的一层称为文档流,文档流是网页的基础

5.1.2 元素的状态

在文档流中
不在文档流中(脱离文档流)

5.1.3 元素在文档流的特点

  • 块元素:
    • 块元素会在页面中独占一行(自上向下垂直排列)
    • 默认宽度是父元素的全部(会把父元素撑满)
    • 默认高度是被内容撑开(子元素)
  • 行内元素:
    • 行内元素不会独占页面的一行,只占自身的大小
    • 行内元素在页面中自左向右水平排列,如果一行之中不能容纳下所有的行内元素,则元素会换到第二行继续自左向右排列(书写习惯一致)
    • 行内元素的默认宽度和高度都是被内容撑开

5.2 盒模型

5.2.1 概念

  • CSS将页面中的所有元素都设置为了一个矩形的盒子
  • 将元素设置为矩形的盒子后,对页面的布局就变成将不同的盒子摆放到不同的位置
  • 每一个盒子都由以下几个部分组成:
    • 内容区(content)
    • 内边距(padding)
    • 边框(border)
    • 外边距(margin)

5.2.2 内容区

  • 内容区(content),元素中的所有的子元素和文本内容都在内容区中排列
  • 内容区的大小由width和height两个属性来设置
    • width 设置内容去的宽度
    • height 设置内容区的高度

5.2.3 边框border

  • 边框(border),边框属于盒子边缘,边框里面属于盒子内部,出了边框都是盒子的外部
  • 边框的大小会影响到整个盒子的大小
  • 要设置边框,需要至少设置三个样式:
    • 边框的宽度:border-widht
    • 边框的颜色:border-color
    • 边框的风格:border-style
  • 属性值设置:见附录

5.2.4 内边距padding

  • 内边距:内容区和边框之间的距离是内边距
  • 一共有四个方向的内边距:
    • padding-top
    • padding-right
    • padding-bottom
    • padding-left
  • 内边距的设置会影响到盒子的大小
  • 背景颜色会延伸到内边距上
  • 一共盒子的可见框的大小,由内容区、内边距 和 边框共同决定
  • padding 内边距的简写属性,可以同时指定四个方向的内边距,规则和border-width一样

5.2.5 外边距margin

  • 外边距不会影响盒子可见框的大小
  • 但是外边距会影响盒子的位置
  • 一共有四个方向的外边距:
    • margin-top 上外边距,设置一个正值,元素会向下移动
    • margin-right 默认情况下设置margin-right不会产生任何效果
    • margin-bottom 下外边距,设置一个正值,下面的元素会向下移动
    • margin-left 上外边距,设置一个正值,元素会向右移动
  • margin也可以设置负值,如果是负值则元素会向相反的方向移动
  • 元素在页面中是按照自左向右顺序排列的
  • 所有默认情况下如果我们设置的左和上外边距则会移动元素自身;而设置下和右外边距会移动其他元素
  • margin的简写属性:margin 可以同时设置四个方向的外边距,用法和padding一样
  • margin会影响到盒子实际占用空间

5.3 盒子的尺寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!--box-sizing  用来设置盒子尺寸的计算方式(设置width和height的作用)
可选值
content-box 默认值,宽度和高度用来设置内容区的大小
bordor-box 宽度和高度用来设置整个盒子可见框的大小
width 和 height 指的是内容区 和 内边距 和边框的总大小-->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box1{
width: 100px;
height: 100px;
background-color: #bfa;
padding: 10px;
border: 10px red solid;

/*
默认情况下,盒子可见框的大小由内容区、内边距和边框共同决定

box-sizing 用来设置盒子尺寸的计算方式(设置width和height的作用)
可选值
content-box 默认值,宽度和高度用来设置内容区的大小
bordor-box 宽度和高度用来设置整个盒子可见框的大小
width 和 height 指的是内容区 和 内边距 和边框的总大小
*/

box-sizing: border-box;
}
</style>
</head>
<body>
<div class="box1"></div>
</body>
</html>

六、布局

6.1 水平布局

  • 元素在其父元素中水平方向的位置由以下几个属性共同决定
    • margin-left
    • border-left
    • padding-left
    • width
    • padding-right
    • border-right
    • margin-right
  • 一个元素在其父元素中,水平布局必须满足以下的等式
    • margin-left+border-left+padding-left+width+padding-right+border-right+margin-right等于其父元素内容区的宽度

6.1.2 过度约束

  • 0 + 0 + 0 + 200 + 0 + 0 + 0 = 800
  • 0 + 0 + 0 + 200 + 0 + 0 + 600 = 800
  • 以上等式必须满足,如果相加结果使等式不成立,则称为过度约束,则等式会自动调整
  • 调整的情况:
    • 如果这七个值中没有为 auto 的情况,则浏览器会自动调整margin-right值,以使等式满足

6.1.3 可以设置为auto的值

  • width
  • margin-left
  • margin-right
  • 如果某个值为auto,会自动调整为auto的那个值以使等式成立
  • 比如:0 + 0 + 0 + auto + 0 + 0 + 0 = 800,会让auto为800
  • 如果将一个宽度和一个外边距设置为auto,则宽度会调整到最大,设置为auto的外边距会自动为0
  • 如果将三个值都设置为auto,则外边距都是0,宽度最大

6.1.4 元素居中效果

  • 如果将两个外边距设置为auto,宽度值固定,则会将外边距设置为相同的值
  • 元素居中效果:auto + 0 + 0 + 200 + 0 + 0 + auto = 800
  • 所以我们经常利用这个特点来使一个元素在其父元素中水平居中
  • 示例:
    • width:xxx px;
    • margin:0 auto;
  • 如果width超过内容区,则会设置margin-right为负值,以使得等式成立
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <style>
    .outer{
    width: 800px;
    height: 200px;
    border: 10px red solid;
    }
    .inner{
    width: 200px;
    height: 200px;
    background-color: #dfa;
    margin-left: auto;
    margin-right: auto;
    }
    </style>

    <div class="outer">
    <div class="inner"></div>
    </div>

6.2 垂直布局

  • 默认情况下父元素的高度被内容撑开
  • 子元素是在父元素的内容区中排列的
  • 如果子元素的大小超过了父元素,则子元素会从父元素中溢出
  • 使用 overflow 属性来设置父元素如何处理溢出的子元素
  • overflow属性可选值
    • visible 默认值,子元素会从父元素中溢出,在父元素外部的位置显示
    • hidden 溢出的内容将会被裁剪不会显示
    • scroll 生成两个滚动条,通过滚动条来查看完整的内容
    • auto 根据需要生成滚动条
  • overflow-x: 设置水平方向
  • overflow-y: 设置垂直方向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<style>
.outer{
background-color: #bfa;
height: 600px;
}
.inner{
width: 100px;
height: 200px;
background-color: yellow;

}

.box1{
width: 200px;
height: 500px;
background-color: #bfa;
}

.box2{
width: 200px;
/* height: 100px; */
height: 400px;
overflow: scroll;
background-color: orange;
}
</style>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
</div>

<div class="box1">
<div class="box2"></div>
</div>

七、外边距的重叠

7.1 概念

  • 相邻的垂直方向外边距会发生重叠现象

7.2 兄弟元素

  • 兄弟元素间的相邻垂直外边距会取两者之间的较大值(两者都是正值)
  • 特殊情况:
    • 如果相邻的外边距一正一负,则取两者的和
    • 如果相邻的外边距都是负值,则取两者中绝对值较大的

7.3 父子元素

  • 父子元素间相邻外边距,子元素的会传递给父元素(上外边距)
  • 父子外边距的折叠会影响到页面的布局,必须要进行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<style>
.box1,.box2{
width: 200px;
height: 200px;
}

.box1{
background-color: #bfa;

/* 设置一个下外边距 */
margin-bottom: 100px;
}

.box2{
background-color: orange;

/* 设置一个上外边距 */
margin-top: 100px;
}

.box3{
width: 200px;
height: 200px;
background-color: #bfa;
/* padding-top: 100px; */
border-top: 1px #bfa solid;
}

.box4{
width: 100px;
height: 100px;
background-color: orange;
margin-top: 99px;

}
</style>

<div class="box1"></div>
<div class="box2"></div>

<div class="box3">
<div class="box4"></div>
</div>

八、行内元素的盒模型

8.1 特点

  • 行内元素不支持设置宽度和高度
  • 行内元素可以设置padding,但是垂直方向的padding不会影响页面的布局
  • 行内元素可以设置border,垂直方向的border不会影响页面的布局
  • 行内元素可以设置margin,垂直方向的margin不会影响布局

8.2 display

  • display:用来设置元素显示的类型
  • 属性可选值:
    • inline : 将元素设置为行内元素
    • block : 将元素设置为块元素
    • inline-block : 将元素设置为行内块元素;行内块,既可以设置宽度和高度又不会独占一行
    • table : 将元素设置为表格
    • none : 元素不在页面中显示

8.3 visibility

  • visibility : 用来设置元素的显示状态
  • 属性可选值:
    • visible : 默认值,元素在页面中正常显示
    • hidden : 元素在页面中隐藏 不显示,但是依然占据页面中的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<style>
.s1{
background-color: yellow;
/* width: 100px;
height: 100px; */

/* padding: 100px; */

/* border: 100px solid red; */

margin: 100px;
}

.box1{
width: 200px;
height: 200px;
background-color: #bfa;
}

a{
/* display: inline; */
/* display: block; */
/* display: inline-block; */
/* display: none; */
visibility: hidden;
display: block;
width: 100px;
height: 100px;
background-color: orange;
}
</style>

<a href="javascript:;"></a>

<span class="s1">我是span</span>

<div class="box1"></div>

九、默认样式

9.1 概念

  • 通常情况下,浏览器都会为元素设置一些默认样式
  • 默认样式的存在会影响到页面的布局,通常情况下编写网页时必须要去除掉浏览器的默认样式(pc端的页面)

9.2 重置样式表reset.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* reset */
html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;}
header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block;}
table{border-collapse:collapse;border-spacing:0;}
caption,th{text-align:left;font-weight:normal;}
html,body,fieldset,img,iframe,abbr{border:0;}
i,cite,em,var,address,dfn{font-style:normal;}
[hidefocus],summary{outline:0;}
li{list-style:none;}
h1,h2,h3,h4,h5,h6,small{font-size:100%;}
sup,sub{font-size:83%;}
pre,code,kbd,samp{font-family:inherit;}
q:before,q:after{content:none;}
textarea{overflow:auto;resize:none;}
label,summary{cursor:default;}
a,button{cursor:pointer;}
h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold;}
del,ins,u,s,a,a:hover{text-decoration:none;}
body,textarea,input,button,select,keygen,legend{font:12px/1.14 arial,\5b8b\4f53;color:#333;outline:0;}
body{background:#fff;}
a,a:hover{color:#333;}

一、 Java变量

  • 局部变量:存储在方法的栈内存中
    • 形参
    • 方法内的局部变量
    • 代码块的局部变量
    • 无法用static修饰
  • 类变量(静态变量):用static修饰的变量,调用时使用类名.变量名(static可以修饰方法名)
  • 成员变量(非静态变量)
1
2
3
4
public class RightDef {
int num1 = num2 + 5; // 静态变量无需考虑声明的位置
static int num2 = 20; // 静态变量
}

二、Java访问控制权限

private default protected public
同一类 y y y y
同一包内的类 y y y
子类 y y
其他包内的类 需要继承 y

三、Java源程序与编译型运行区别

四、程序控制

4.1 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 第一种
while(){

}

// 第二种
do{

}while();

// 第三种
for(初始化;布尔表达式;更新){

}

// Foreach语法,用于数组和容器
for(float x : f){ // f是数组或容器
System.out.println(x);
}

4.2 条件

1
2
3
4
5
6
7
8
9
10
11
if(){
if(){

}
else if(){

}
}
else{

}

4.3 选择

1
2
3
4
5
6
7
8
9
switch(expression){
case value1: //语句
break;
case value2: // 语句
break;
default: // 语句
break;
}

五、数组

5.1 概念

  • 数组属于引用数据类型
  • 包含:数组名、下标、元素

5.2 特点

  1. 数组是有序排列的
  2. 数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
  3. 创建数组对象会在内存中开辟一整块连续的空间
  4. 数组的长度一旦确定,就不能修改

5.3 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
int[] ids;  //数组声明
ids = new int[]{1,2,3}; //静态初始化,初始化同时进行赋值,new int[]不能省略
int[] ids = {1,2,3};

//动态初始化,初始化与元素赋值分开
String[] names = new String[5];

// 获取数组长度
int len = ids.length;

// 数组元素的调用
names[0] = "zs";
names[1] = "ls";

5.4 数组元素的默认初始化值

  • 整型:0
  • 浮点型:0
  • char:0或’\u0000’,而非’0’
  • boolean:false
  • 数组元素是引用数据类型时:null

5.5 多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 二维数组静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
int[] arr1[] = new int[][]{{1,2,3},{4,5},{6,7,8}};
int[] arr1[] = {{1,2,3},{4,5},{6,7,8}};

// 动态初始化1
String[][] arr2 = new String[3][2];

// 动态初始化2
String[][] arr2 = new String[3][];
System.out.println(arr2[1][0]); //由于没有初始化,所以会报空指针异常

arr2[1] = new String[4]; // 给arr[1]初始化后,再来输出就不会报错
System.out.println(arr2[1][0]);

// 错误形式
// int[][] arr3 = new int[4][3]{{1,2,3},{4,5},{6,7,8}}

5.5.1 多维数组的默认初始值

1
2
3
4
int[][] arr = new int[4][3];
System.out.println(arr[0]); // 地址值
System.out.println(arr[0][0]); // null

5.6 数组的遍历

1
2
3
4
5
// foreach方式
int[] a = new int[]{1,2,3}
for(int element:a)
System.out.println(element);

5.7 Array类

5.7.1 常用方法

  • java.util.Array类的所有方法均为静态方法
  • boolean equals(int[] a,int[] b):判断两个数组是否相等
  • String toString(int[] a):输出数组信息
  • void fill(int[] a,int val):将指定值填充到数组中
  • void sort(int[] a):对数组进行排序(快速排序)
  • int binarySearch(int[] a,int key):对排序后的数进行二分法检索指定的值

5.8 数组常见异常

  • 空指针:NullPointerException
  • 数组下标越界:ArrayIndexOutOfBoundsException

六、类和对象

6.1 类

6.1.1 构造器

  • 一个在创建对象时被自动调用的特殊方法
  • 名称必须要和类名相同
  • 构造器调用顺序是客->主
  • 每个类都有构造方法,没有显示的为类定义构造方法,Java编译器会提供一个默认的构造方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Rock2{
    Rock(int i){
    System.out.print("Rock"+i+" ");
    }
    }

    public class SimpleConstructor2(){
    public static void main(String args[]){
    for(int i=0;i<8;i++)
    new Rock2(i);
    }
    }

    // Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7

6.1.2 类的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Dog{
String breed;
int age;
String color;
void barking(){
}

void hungry(){
}

void sleeping(){
}
}

6.1.3 类中的变量类型

  • 局部变量
  • 成员变量
  • 类变量

6.2 对象

  • 声明:声明一个对象,包括对象名和对象类型
  • 实例化:使用new来创建一个对象
  • 初始化:new创建对象时,会调用构造函数
1
2
3
4
5
6
7
8
9
10
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}

七、面向对象特性

7.1 封装

  • 创建类以实现现实中抽象事物属性以及方法的封装

7.2 继承

7.2.1 继承的实现

1
2
3
4
5
6
class father{

}
class son extends father{

}

7.2.2 继承的特性

  • 子类拥有父类非private的属性、方法
  • 所有的类都继承于java.lang.Object

7.2.3 Super关键字

  1. 在子类的成员方法中,访问父类的成员变量。
  2. 在子类的成员方法中,访问父类的成员方法。
  3. 在子类的构造方法中,访问父类的构造方法。

7.3 多态

7.3.1 多态实现的必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

7.3.2 多态的实现方式

  • 重写
  • 接口
  • 抽象类和抽象方法

八、抽象类和抽象方法

8.1 抽象类

  • 抽象类用来描述一种类型应该具备的基本特征与功能,具体如何去完成这些行为由子类通过方法重写来完成
  • 抽象方法指只有功能声明,没有功能主体实现的方法
  • 有抽象方法的类一定是抽象类
  • 抽象类无法直接创建对象,只能被子类继承后,创建子类对象
  • 抽象方法必须被子类重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + number;
}
public String getName()
{
return name;
}
public String getAddress()
{
return address;
}
public void setAddress(String newAddress)
{
address = newAddress;
}
public int getNumber()
{
return number;
}
}

8.3 抽象方法

  • 抽象方法没有方法体,子类继承后必须重写该方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public abstract class Employee
    {
    private String name;
    private String address;
    private int number;

    public abstract double computePay();

    //其余代码
    }

九、Java接口

9.1 接口与类的区别

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口中的成员变量只能是 public static final 类型。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承

9.2 接口的实现

  • 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。

  • 类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Animal {
public void eat();
public void travel();
}

public class MammalInt implements Animal{

public void eat(){
System.out.println("Mammal eats");
}

public void travel(){
System.out.println("Mammal travels");
}

public int noOfLegs(){
return 0;
}

public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}

9.4 接口的特性

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

9.5 接口的继承

  • 一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}

// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}

// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
  • Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
  • 相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。

9.6 接口的多继承

  • 在Java中,类的多继承是不合法,但接口允许多继承。

  • 在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:

    1
    public interface Hockey extends Sports, Event

十、Java枚举

10.1 枚举的实现

  • Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。

  • Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    enum Color
    {
    RED, GREEN, BLUE;
    }

    public class Test
    {
    // 执行输出结果
    public static void main(String[] args)
    {
    Color c1 = Color.RED;
    System.out.println(c1);
    }
    }

    // 结果为RED

10.2 迭代枚举元素

1
2
3
4
5
6
7
8
9
10
11
enum Color
{
RED, GREEN, BLUE;
}
public class MyClass {
public static void main(String[] args) {
for (Color myVar : Color.values()) {
System.out.println(myVar);
}
}
}

10.3 枚举中的方法

  • values() 返回枚举类中所有的值。
  • ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
  • valueOf()方法返回指定字符串值的枚举常量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
enum Color
{
RED, GREEN, BLUE;
}

public class Test
{
public static void main(String[] args)
{
// 调用 values()
Color[] arr = Color.values();

// 迭代枚举
for (Color col : arr)
{
// 查看索引
System.out.println(col + " at index " + col.ordinal());
}

// 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
System.out.println(Color.valueOf("RED"));
// System.out.println(Color.valueOf("WHITE"));
}
}

/*结果
RED at index 0
GREEN at index 1
BLUE at index 2
RED
*/

10.4 枚举的作用

  • 封装一组常量
  • 对传入的参数进行约束和限制

十一、Java异常

11.1 异常分类

  1. 检查性异常: 不处理编译不能通过
  2. 非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台
  3. 运行时异常: 就是非检查性异常
  4. 非运行时异常: 就是检查性异常

所有的异常类是从 java.lang.Exception 类继承的子类

11.2 异常捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 单捕获
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}

// 多异常捕获
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}

11.3 异常抛出

11.3.1 throws关键字

  • 用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,需要由方法的调用者进行异常处理
  • 用在方法声明后面,跟的是异常类名
  • 可以跟多个异常类名,用逗号隔开
  • 表示抛出异常,由该方法的调用者来处理
  • throws表示出现异常的一种可能性,并不一定会发生这些异常

11.3.2 throw关键字

  • 用来抛出一个具体的异常类型。

  • 用在方法体内,跟的是异常对象名

  • 只能抛出一个异常对象名
  • 表示抛出异常,由方法体内的语句处理
  • throw则是抛出了异常,执行throw则一定抛出了某种异常
  • 如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
  • 执行到 throw 语句则后面的语句块不再执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

package constxiong.interview;

import java.io.IOException;

public class TestThrowsThrow {

public static void main(String[] args) {
testThrows();

Integer i = null;
testThrow(i);

String filePath = null;
try {
testThrow(filePath);
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 测试 throws 关键字
* @throws NullPointerException
*/
public static void testThrows() throws NullPointerException {
Integer i = null;
System.out.println(i + 1);
}

/**
* 测试 throw 关键字抛出 运行时异常
* @param i
*/
public static void testThrow(Integer i) {
if (i == null) {
throw new NullPointerException();//运行时异常不需要在方法上申明
}
}

/**
* 测试 throw 关键字抛出 非运行时异常,需要方法体需要加 throws 异常抛出申明
* @param i
*/
public static void testThrow(String filePath) throws IOException {
if (filePath == null) {
throw new IOException();//运行时异常不需要在方法上申明
}
}
}

11.4 自定义异常类

  • 所有异常都必须是 Throwable 的子类。
  • 如果希望写一个检查性异常类,则需要继承 Exception 类。
  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// 文件名InsufficientFundsException.java
import java.io.*;

//自定义异常类,继承Exception类
public class InsufficientFundsException extends Exception
{
//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}

// 文件名称 CheckingAccount.java
import java.io.*;

//此类模拟银行账户
public class CheckingAccount
{
//balance为余额,number为卡号
private double balance;
private int number;
public CheckingAccount(int number)
{
this.number = number;
}
//方法:存钱
public void deposit(double amount)
{
balance += amount;
}
//方法:取钱
public void withdraw(double amount) throws
InsufficientFundsException
{
if(amount <= balance)
{
balance -= amount;
}
else
{
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
//方法:返回余额
public double getBalance()
{
return balance;
}
//方法:返回卡号
public int getNumber()
{
return number;
}
}

// 下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw() 方法
//文件名称 BankDemo.java
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try
{
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e)
{
System.out.println("Sorry, but you are short $"
+ e.getAmount());
e.printStackTrace();
}
}
}

/*Output:
Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
at CheckingAccount.withdraw(CheckingAccount.java:25)
at BankDemo.main(BankDemo.java:13)
*/

十二、Java内部类

12.1 非静态内部类

  • 非静态内部类在类中地位等同于成员变量
  • 非静态内部类实例的创建必须依赖外部类对象
  • 非静态内部类可以访问到外部类的成员变量和成员方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class OuterClass {
private int age = 10;

private class InnerClass{
private int age = 11;
public void print(){
System.out.println(age); //11
System.out.println("内部类提升封装性,代码结构更合理");
}
}

public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
innerClass.print();
}
}

12.2 静态内部类

  • 静态内部类使用static对内部类进行标注
  • 静态内部类无法访问外部类的成员变量和成员方法,但可以访问静态成员变量
  • 静态内部类实例无需通过外部类实例进行创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OuterClass {

private static class InnerClass{
public void print(){
System.out.println("内部类提升封装性,代码结构更合理");
}
}

public void printOuter(){
System.out.println("我是外部类方法");
}

public static void main(String[] args) {
InnerClass innerClass = new OuterClass.InnerClass();
innerClass.print();
}
}

12.3 匿名内部类

  • 不显示声明类继承接口进行方法重写后调用
  • 通过接口引用+匿名内部类在”{}”中重写方法,完成对象实例的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface MyInterface {
void print();
}

public class NoneClass {
public static void main(String[] args) {
MyInterface myInterface = new MyInterface() {
public void print() {
System.out.println("匿名内部类");
}
};
myInterface.print();
}
}

12.4 使用内部类的原因

  • 内部类提升封装性,隐藏部分操作,代码结构更合理

一、基础

1.1 Hello案例

1.2 数据绑定

  • v-bind:单向绑定,数据只能从data流向页面
  • v-model:双向绑定,数据不仅能从data流向页面,还可以从页面流向data
  • 单向绑定和双向绑定的区别:
    • v-model一般应用再表单类元素上
    • v-model:value可以简写为v-model,因为v-model默认收集的就是value值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 花括号里为表达式 -->
<div id="app">
单向绑定:<input type="text" v-bind:value="message"><br/>
双向绑定:<input type="text" v-model:value="message">
</div>

<script type="text/javascript">
Vue.config.productionTip = false;

var app = new Vue({
el: '#app', //根据标签id进行绑定
data: {
message: 'Hello Vue!',
}
});
</script>

1.3 MVVM模型

  • M:模型,对应data中的数据
  • V:视图,页面模板
  • VM:视图模型,Vue实例,以后一般使用”vm“作为变量名接收Vue实例

1.4 数据代理

1.4.1 object.defineProperty

  • 动态为对象绑定属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let number = 18
let person = {
name: '张三',
sex: '男'
}

// 动态为person对象绑定属性
Object.defineProperty(person,'age',{
value: 18 // 设置属性值
enumerable:true, // 控制属性是否可以枚举,默认为false
writeable:true, // 控制属性是否可以被修改,默认为false
configurable:true, // 控制属性是否可以被删除,默认为false

// 当动态修改number值时,age便会发生改变
get(){
return number;
},

// 当动态修改age属性值时,number就发生改变
set(value){
number = value;
}
})

1.4.2 vue数据代理

  • Vue实例中的data对象中的属性就是使用到了object.defineProperty的getter和setter的操作
  • 在控制台打印vue实例对象时,就可以看到代理的data数据

1.5 事件处理

1.5.1 事件基本使用

  • 使用v-on:xxx或@xxx绑定事件,其中xxx是事件名
  • 事件的回调需要配置在methods对象中,最终会显示在vm上
  • methods中配置的函数,不需要箭头函数!否则this就不是vm了
  • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
  • @click=”demo”和@click=”demo($event)”效果一样,但后者可以自定义传参

1.5.2 事件修饰符

Vue事件修饰符包括:

  1. prevent:阻止默认事件(常用)
  2. stop:阻止事件冒泡
  3. once:事件只触发一次,即下一次触发就没有效果了
  4. capture:使用事件的捕获模式
  5. self:只有event.target是当前操作的元素时才触发事件
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕

Vue的事件修饰符使用方式为:@click.prevent=""

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="root">
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
</div>

<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
method:{
showInfo(e){
alert('同学你好!')
}
}
})
</script>

1.5.3 键盘事件

Vue中常用的键盘别名

  • 回车:enter
  • 删除:delete(捕获“删除”和“退格”键)
  • 空格:space
  • 上:up
  • 下:down
  • 左:left
  • 右:right

键盘事件的捕获方式如下:

  • @keyup.xxx:键盘按下后弹起

  • @keydown.xxx:键盘按下

注意事项如下:

  • Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要将双驼峰命名转为xxx-xxx(短横线命名)
  • 系统修饰键(用法特殊):ctrl、alt、shift、meta
    • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发,如@keyup.ctrl.y
    • 配合keydown使用,正常触发事件

1.6 计算属性

计算属性是对vue属性进行计算操作后设定的属性,它有以下特点:

  • 计算属性和data中的属性不是一个概念,使用vm._data无法访问到计算属性
  • 计算属性是对data属性计算后得到的属性
  • computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会从新计算 computed 的值;
  • 不支持异步,当 Computed 中有异步操作时,无法监听数据的变化
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用computed
  • 如果 computed 属性的属性值是函数,那么默认使用 get 方法,函数的返回值就是属性的属性值;在 computed 中,属性有一个 get 方法和一个 set 方法,当数据发生变化时,会调用 set 方法
  • computed中的函数必须用return返回
  • computed中不能对data中的属性进行赋值操作,如果对data中的属性进行赋值,data中的属性发生变化,从而触发computed中的函数,就会形成死循环。

原始的计算属性写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root">
姓:<input type="text" v-model="firstName"/><br/>
名:<input type="text" v-model="lastName"/><br/>
全名:<div>{{fullName}}</div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstName:'zhang',
lastName:'san'
},
computed:{
fullName:{
get(){
return this.firstName + '-' + this.lastName
}
}
}
})
</script>

简化后的写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="root">
姓:<input type="text" v-model="firstName"/><br/>
名:<input type="text" v-model="lastName"/><br/>
全名:<div>{{fullName}}</div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstName:'zhang',
lastName:'san'
},
computed:{
fullName(){
return this.firstName + '-' + this.lastName
}
}
})
</script>

1.7 监视属性

监视属性具有以下特点

  • 更多的是「观察」的作用,用于监听data中的数据变化,只在被监听的属性值发生变化时执行;
  • watch不支持缓存,当一个属性发生变化时,它就会触发相应的操作;
  • watch支持异步监听
  • watch监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
  • watch监听数据必须是 data 中声明的或者父组件传递过来的 props 中的数据,当发生变化时,会触发其他操作

1.7.1 计算属性与监视属性的区别

computer是计算属性,且属性值具有缓存性。同时,computer还依赖于其他属性的计算值,当计算值发生变化时返回内容。当需要依赖于其他属性的计算值的变化来动态获取值的时候使用computer

watch只要监听到值发生变化就会执行回调,同时在回调过程中执行一些逻辑操作。当我们需要执行复杂的逻辑操作时,使用watch

1.8 绑定样式

一、JS基本操作

1.1 JS引入方式

  • 第一种:内部标签<script></script>
  • 第二种:外部引入<script type="text/javascript" src="./xxx.js"></script>

1.2 JS的6种数据类型

  • number
  • string
  • boolean
  • object
  • function 函数

1.3 JS特殊值

  • Null 空值
  • Undefine 未定义
  • NAN 非数值

1.4 JS数组

  • var 数组名 = [1,’abc’]

1.5 JS循环

1.5.1 for

1
2
3
4
5
6
7
for(语句1;语句2;语句3){
代码块
}

for(变量 in 对象名/数组名){
代码块
}

1.5.2 while

1
2
3
4
5
6
7
while(条件){
代码块
}

do{
代码块
}while(条件);

1.6 JS函数定义方式

  • 第一种:function 函数名(形参列表){函数体}
  • 第二种:var 函数名 = function(形参列表){函数体}
  • 函数不允许重载
  • arguments:存放形参的数组,可以在函数体中直接调用,形参列表可以不写

1.7 JS对象的定义

1.7.1 第一种

1
2
3
4
var 变量名 = new Object();
变量名.属性名 = 值
变量名.函数名 = function(){}
this关键字可以在函数体中使用

1.7.2 第二种

1
2
3
4
5
var 对象名 = {
属性名:值,
属性名:值,
函数名:function(){}
};

1.7.3 对象的访问

  • 对象名.属性名/函数名()

1.8 JS入口函数

1
2
3
window.onload = function(){
// 开始编写JS代码
}

1.9 JS数组

1
2
3
4
5
6
7
8
9
10
11
12
// 创建数组对象
var arr = new Array();
arr[0] = 1;
arr[1] = 2;

//读取不存在的索引,不会报错而会返回undefined
console.log(arr[3])

//打印数组长度,length表示最大的索引
//length可以修改,长了填充空值,短了截断
console.log(arr.length)

二、JS函数

2.1 函数的声明

1
2
3
function 函数名(形参列表){
函数体
}

2.2 函数对象

  • 在JS中,函数名就代表了函数对象

2.3 匿名函数

2.3.1 语法

1
2
3
function(){
函数体
}

2.4 立即执行函数

2.4.1 语法

1
2
3
(function(形参列表){
函数体
})(实参列表)

2.4.2 特性

  • 立即执行函数只执行一次

2.5 回调函数

2.5.1 概念

  • 并非自己调用,而是由浏览器自动调用的函数
  • 一般是作为函数的参数进行使用,可以通过匿名函数的写法进行传递,也可以直接传递函数名

2.5.2 语法

1
2
3
4
5
6
7
8
9
10
注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
//或者
function click() { // 它就是回调函数
alert("Btn 1 Clicked");
}
$("#btn_1").click(click);

三、JS对象

3.1 对象的分类

  • 内建对象
  • 宿主对象
  • 自定义对象

3.2 对象的定义

1
2
3
4
5
6
7
8
9
10
11
var 变量名 = new Object();
变量名.属性名 = 值
变量名.函数名 = function(){}
this关键字可以在函数体中使用

// 或者
var 对象名 = {
属性名:值,
属性名:值,
函数名:function(){}
};

3.3 this关键字

  1. 当以函数的形式调用时,this是window
  2. 当以方法的形式调用时,谁调用方法,this就是谁
  3. 当以构造函数的形式调用时,this就是新创建的那个对象

3.4 构造函数

1
2
3
4
5
6
7
8
function Person(name,age,gender){
alert(this);
this.name = name; //添加属性name
this.age = age;
this.gender = gender;
}

var per = new Person();

3.4.1 执行流程

  1. 立刻创建一个新的对象
  2. 将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
  3. 执行函数中的代码
  4. 将新建的对象作为返回值返回

3.5 原型对象

  • JS中万物皆对象
  • 每个对象都具有proto,用于指向其构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法
  • 方法是一个特殊的对象(可以把方法的声明当作构造函数),它同时具有proto指针,以及属性prototype

四、Dom操作

4.1 操作元素节点

函数 解释
getElementById() 通过id属性获取一个元素节点对象
getElementsByTagName() 通过标签名获取一组元素节点对象
getElementsByName() 通过name属性获取一组元素节点对象
  • 获取到元素对象/对象数组后,这些对象包含以下属性
    • 对象名.innerHtml
    • 对象名.id
    • 对象名.name
    • 对象名.value
    • 对象名.className
    • 对象名.时间名=function(){}

五、JS事件

5.1 事件的概念

  • 电脑输入设备与页面进行交互的响应

5.2 常用的事件

  • onload:加载完成事件,页面加载完成之后,常用于做页面js代码初始化操作
  • onclick:单击事件,常用于按钮点击的响应操作
  • onblur:失去焦点事件,常用于输入框失去焦点后验证其输入内容是否合法
  • onchange:内容发生改变事件,常用于下拉列表和输入框内容发生改变后操作
  • onsubmit:表单提交事件,常用于表单提交前,验证所有表单是否合法

5.3 事件的注册

5.3.1 静态注册

  • 通过html标签的事件属性直接赋予事件响应后的代码

5.3.2 动态注册

  • 是指先通过js代码得到标签的dom对象,然后再通过:dom对象.事件名=function(){}这种形式赋予事件响应后的代码

5.3.3 步骤

  1. 获取标签对象
  2. 标签对象.事件名=function(){}

5.4 事件对象event

  • 在给元素绑定事件时,写上函数参数event
  • 使用此event可以在触发事件时达到某些效果
  • 为兼容IE8,函数体中可以写上此语句:event = event||window.event;
1
2
3
4
5
6
7
var box1 = document.getElementById("box1");
box1.onclick = function(event){ //参数名一般写做event或e
if(event.target.className == "link"){ //判断子元素中是否有a链接元素
alert("我是box1的单击事件");
}

}

5.5 事件的冒泡

  • 给覆盖在父元素上的子元素都绑定上事件,点击子元素时,会先触发子元素的事件,然后再触发父元素的事件
  • event.cancelBubble = true;可以用来取消父元素的冒泡
1
2
3
4
5
var box1 = document.getElementById("box1");
box1.onclick = function(){ //回调函数,无需自己调用
alert("我是div的单击响应函数");
event.cancelBubble = true;
}

5.6 事件的委派

  • 给各子元素共同的祖先元素绑定事件,这样后代元素上的事件触发时,则会统一触发祖先绑定的事件
  • 这样做无需一个个给子元素单独绑定事件,比较方便
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
window.onload = function(){
var ul = document.getElementsById("ul");
ul.onclick = function(){
alert("我是ul的单击事件")
}

}
</script>

<ul id="ul">
<li><a href="">1</a></li>
<li><a href="">2</a></li>
<li><a href="">3</a></li>
</ul>