mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
[feat]add 为什么 Java 中只有值传递?
This commit is contained in:
parent
878067bfa6
commit
b3cf01f104
@ -44,6 +44,7 @@
|
||||
|
||||
**重要知识点详解:**
|
||||
|
||||
- [为什么 Java 中只有值传递?](docs/java/basis/why-there-only-value-passing-in-java.md)
|
||||
- [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basis/反射机制详解.md)
|
||||
- [代理模式详解:静态代理+JDK/CGLIB 动态代理实战](docs/java/basis/代理模式详解.md)
|
||||
- [常见的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥区别?](docs/java/basis/java基础知识总结)
|
||||
|
@ -148,7 +148,7 @@ module.exports = config({
|
||||
"java基础知识总结",
|
||||
{
|
||||
title: "重要知识点",
|
||||
children: ["反射机制详解", "代理模式详解", "io模型详解"],
|
||||
children: ["why-there-only-value-passing-in-java", "反射机制详解", "代理模式详解", "io模型详解"],
|
||||
},],
|
||||
},
|
||||
{
|
||||
|
1
docs/java/basis/images/java-value-passing-01.drawio
Normal file
1
docs/java/basis/images/java-value-passing-01.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2021-11-24T03:52:30.002Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="bvsmkjngkIq_COWrT3rC" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7VfbjpswEP0aS+1DKi6BhEcgJG2lqqtut7vtS+WAAygOzhrn1q/vGEy4ZndVKbsvVR7iOR57xnPOgEGmvzkuON4mX1hEKDK06IjMGTIMXdds+JPIqUQM3VZIzNNIedXAbfqHKFBT6C6NSN5yFIxRkW7bYMiyjISihWHO2aHttmK0HXWLY9IDbkNM++h9GomkRKfGpMY/kjROqsi67ZQzG1w5q5PkCY7YoQGZATJ9zpgoR5ujT6isXlWXct38wuw5MU4y8ZIFEfua5d/uKLF/Z/78093NXbgYqV32mO7UgVFgoamFXB8FY+T5yJkUyBx5MJgXiPsZ7/FCEoMMm0Jwb8nlAcVJVc1+3MlTeSuWiVFecOqCg25vQRhePQ+juPiXoWbIgZhTNNVk/CpUGQYycEzkWqqS50CGIEe5QSI2FAAdhrngbE18RhkHJGMZkYmklHYgTNM4AzOE8hHAvT3hIgXiXTWxSaNIhvEOSSrI7RaHMuYBdA4YZ7ssIrKyGlgULwn1cLiOC7wbHKqghK3byq5ckGF6xa/YU2CRMhl7ZMpt8zURYaLO1ae74g7yJscGpOhfELYhgp/ARc0aEyXFQ61kfaqwpKlihWHVPPF5q1pfMFASG5bbd5NnHv/z6zG4If6PKIzu3fVIt/t6UxKSVXmJiAwTRNRTULbbGO+kTNwAOYFS7NR4X+0O2ZYBlOC6Oqr51C8w3pBYTHGeV3KrulrvEtbUHNC8WhE7DHsChZlo4iw17ZxVj88B1i9S7KgWUY9bc4Bwe4Bw62qET69HuP6fcCBcaxEOHWb1KZ+8JuVy/QXK8y3O/p3yZcE3vCdcY5Dvcvc345vokUUmQ3w79sTE9lUaXH/zDjfGV+pw/Azdb9zer0R3p72H3uCv291Db/BO8UkWufLqLW9XsrRp+RTEXPThBg1QEn56UFeqwvgpjQ9WZc6OzcnZqbKOqXhojBurwKoXSeO8JoJbvsqYcZGwmGWYBjWqyJN+T1MHJ2c7HpIXPBGhAjERz/ZSXwwNtq0nyOaEwgVy3054SAEqwg1LiwZVWhu3tda9BJbHVGua3xmdbbqSHXf2KavQ2wekgU8Nt610yC9na3aytawnszK7WbXcYVDGrxvjXP6hXgGz/nAr3evvXzP4Cw==</diagram></mxfile>
|
BIN
docs/java/basis/images/java-value-passing-01.png
Normal file
BIN
docs/java/basis/images/java-value-passing-01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
1
docs/java/basis/images/java-value-passing-02.drawio
Normal file
1
docs/java/basis/images/java-value-passing-02.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2021-11-24T03:33:09.587Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="pc9xWnTv6V551KKFcp_6" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbc9o4FP41mtl9oGNZvj5iAnSzvWQ2k2mzLzvCFrY3xmJlEaC/fiVbvjuEUmiTTuEB69P9fN85OjIATVa7OcPr6D0NSAJ0LdgBdAV0HULNEj8S2ReIDi2FhCwOVKsauI2/EAVqCt3EAclaDTmlCY/XbdCnaUp83sIwY3TbbrakSXvWNQ5JD7j1cdJHP8UBjwrU0e0af0viMCpnhpZb1Kxw2VjtJItwQLcNCE0BmjBKefG02k1IIq1X2qXoN3uitloYIyk/psPi72gzG7OHT/q7PY4/hNtr+HGkF6M84mSjNqwWy/elBRjdpAGRg0CAvG0Uc3K7xr6s3QrSBRbxVaKqwwRnmXquNpwXHgj3I1VYxkkyoQll+QwowMRZ+rIRZ/SBNGos3yGLpajpb1bt/5EwTnYNSG1+TuiKcLYXTVQtKiWlpDjSNQVsa2J1S2FRg1RkKxArMYXV4LW9xYMy+deYv29uEgj9qSJlPKIhTXEyrVGvJkQTpbrNO0rXysD/Es73ypnwhtM2ScKGbP9Z9c8L97LwxiyLV7tm5dVelZ4kIaMb5pMD+zSU52IWEn6gHVRuJY1wkFNGEszjx7aTDtGTdx0zhveNBmsapzxrjHwjgVoqut2WimW2PeuZ5iNodpRRLKDWSbWT06VjDHiulXDpXTRfXK0p678NLStGWa6KsWigo/WurhRPofzFcqm/gakJvCsw1uWDMwOO/ns5vFhtMUPR/gfECwIDk9hD8cK1bISt88QLRXpJqtEPFnAoWJgXixVaz9Q/ZaywXnesGEHYOYdfQrSwLhct8lgxngJ3+hJjxXJJLH8wtwhsd6EdlOrJsWIEkdmPFvb3jBb2NxOOtCHCc7I14FjHUiwsyNs8tplIaUo6tCkIJ3GYiqIveCEC9yQfsUjNx6piFQdBHuWGhNOJfKIy5pIeQ1NbVSEPuueRgKF33drunxjGgAT0S0kADmng+yf3JnECY8gBHX2BrDMd1si129Z39Dd9F3RQ3/yVq36L/QP6Mc3+ukuI9U86mf1xd3Pnz0ewb34ZIIX/TMDUAN4EiEUXIdMTD7McGV/jRzyXl97SwRbsGG+FlvDW3E17DmvITM4VczrA0eT85VTFNGIFLgJj84V6boIXJPGw/xDmeHfyhiNbqtyQmJd/8zG5yAGonHuEtK5kzyBA6LbdX0cD+aIz4P7wDO6/0e4+/MnuJ/NJdm3S+/V79nZ/zNWepMFYviOp7RngLKrCQYN5id9gLthMc0QcD5UeyvciejNdrHLHIl3Un8sXyS7mnxvPzW6iWPeShbJT96w35XfwPUL+qZjupIwD1Pd5bvBoHjjIvy7Z7CWHvTjWGaHInlWnA0mm4XRPo/Y4RXbdG+eEdHNQe+jn195rUxLSzqOk7jgXVpL5S0kvTUmjc0mpN9CFtfTsJdivTow6i0LFZyCxUn2zNU5Pv0CDqQVcE3gamNrA84BjNC5WxdDt6V7HfeuJ9OkoVZdqgUZbLQP3KdPqq/6E+5Qo1v/DFGqr/85C0/8B</diagram></mxfile>
|
BIN
docs/java/basis/images/java-value-passing-02.png
Normal file
BIN
docs/java/basis/images/java-value-passing-02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
1
docs/java/basis/images/java-value-passing-03.drawio
Normal file
1
docs/java/basis/images/java-value-passing-03.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2021-11-24T07:29:28.493Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="YfKlEbrvOOY6jty1IFoX" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbd+I2EP41Pqd9IMeWfOPREMh2l223zTbZ5k3YwlYxFivErb++ki3jKw7pQki7PTygGY1u8818GtsaHC52dwwto480wLEG9GCnwVsNAMPQbfEnNftMAwxbaUJGAmVVKO7JX1gpdaVdkwCvKoac0piTZVXp0yTBPq/oEGN0WzWb0bi66hKFuKG491Hc1D6SgEeZ1gVOoX+HSRjlKxt2P+tZoNxYnWQVoYBuSyo40uCQUcqz1mI3xLH0Xu6XbNz4SO9hYwwn/JQB06doPfbY/BFM9oj8HG7fG7/0QDbLBsVrdWC1Wb7PPcDoOgmwnMTQ4GAbEY7vl8iXvVsButBFfBGr7jBGq5VqHw6cCnPM/UgJMxLHQxpTlq4AA4TdmS+NOKNzXOqxfRdPZ6KneVh1/g1mHO9KKnX4O0wXmLO9MFG9wDWzISoUe0BXyGwLYIGtdFEJVOgoJVLBFB4mL/wtGsrl7e7/6et+bD6YCXE/333++OCaH/xNz254Gwci/JRIGY9oSBMUjwrtoMBDF1JhM6F0qfz7J+Z8r3IJrTmtYiRcyPZf1PhU+EMKN1Yu3u7Knbd7JR3FYEXXzMcdUaYczxELMe9wh7KTPuhElOEYcbKppmgbOOlQjzG0LxksKUn4qjTzJ6koAgXm3KMCxbSqefWMuWvXwiJbvwiSw0FOipsuh1bS1o65TC2a7q2IKPvrmuYdvVUaE54wAHC5KzpFK5T/O4LohPygjSzNG2n9kWy4Y80FP+bzi+1mS2QDrsAWsxm2/Va2CJz+VO+M1NPZwrEqoIImVRhtVGFdiims74Mp7CZTtNoZzrmp4lR0urZ9kXx8ilAS/p+SxdWc399GnqPlpOxfKClbYXe+GXaot8Gegq1r8iY5DWLhQV7FsYpEQhNcg02pUEzCRIi+wAUL/UDiQUT966mOBQmClE3aAqfGMKKTcAmPqaujKmox+ucJARM0YqBJzGZLCIBLhYBxPAZWS5T889Rf8XWAU8jTyaaswQml4MiWulr+BxZ2A7Mt/10wheKJ7yzgQ9u9sWrwA3DTQgImbCGBg+XZ72b3P3c3tx4T/KureOO6ZXyXR8/OHUvMVjQBadEwuNU80Fo0XJk0sCFow2kjjb7tQHQm0nBqUXD1Qt75PsgCvvlCvmvbZy/ks4w0nsnIK5fxr5ORbq2Ec1sy0nnNjDz+MuWCFdyENPVvh5tfqaCrhYLdEgmvXMrlRUDXC3GcBJ78siAkXzqXZE++iPGm+gwcjHeEfym1S6OEVAySwmHMs1fK5ctAeCKxl9C2OtL+ZdVis7yrsUd2TGXUURbW2arOQpkbGvO8tFoFLytW64/BVsX8m4vVx99B4gS/7Z5+ZZ8eJpt36MN80cvTu5QZ8g6zNG+ojUxtMNT6jrrVBqIxTjXee7RBd/IbXpnunmdUwxaMmjJi49WIKS/QvljT1QQ4Yv18qWwZsYM+1Lzm+9K38Y4kRlMcD5A/D1N9ffHSKxNbySXyHaS/dE4ukoHKtXtQr5P5ER5uSffj1AyNxrN2Czu7LQlrvPyeFmLxHTSL1+JzMhz9DQ==</diagram></mxfile>
|
BIN
docs/java/basis/images/java-value-passing-03.png
Normal file
BIN
docs/java/basis/images/java-value-passing-03.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
@ -1,22 +1,57 @@
|
||||
---
|
||||
category: Java
|
||||
tag:
|
||||
- Java基础
|
||||
---
|
||||
|
||||
|
||||
|
||||
# 为什么 Java 中只有值传递?
|
||||
|
||||
首先,我们回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。
|
||||
开始之前,我们先来搞懂下面这两个概念:
|
||||
|
||||
**按值调用(call by value)** 表示方法接收的是调用者提供的值,**按引用调用(call by reference)** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。
|
||||
- 形参&实参
|
||||
- 值传递&引用传递
|
||||
|
||||
**Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。**
|
||||
## 形参&实参
|
||||
|
||||
**下面通过 3 个例子来给大家说明**
|
||||
方法的定义可能会用到 **参数**(有参的方法),参数在程序语言中分为:
|
||||
|
||||
> **example 1**
|
||||
- **实参(实际参数)** :用于传递给函数/方法的参数,必须有确定的值。
|
||||
- **形参(形式参数)** :用于定义函数/方法,接收实参,不需要有确定的值。
|
||||
|
||||
```java
|
||||
String hello = "Hello!";
|
||||
// hello 为实参
|
||||
sayHello(hello);
|
||||
// str 为形参
|
||||
void sayHello(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
```
|
||||
|
||||
## 值传递&引用传递
|
||||
|
||||
程序设计语言将实参传递给方法(或函数)的方式分为两种:
|
||||
|
||||
- **值传递** :方法接收的是实参值的拷贝,会创建副本。
|
||||
- **引用传递** :方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。
|
||||
|
||||
很多程序设计语言(比如 C++、 Pascal )提供了两种参数传递的方式,不过,在 Java 中只有值传递。
|
||||
|
||||
## 为什么 Java 只有值传递?
|
||||
|
||||
**为什么说 Java 只有值传递呢?** 不需要太多废话,我通过 3 个例子来给大家证明。
|
||||
|
||||
### 案例1:传递基本类型参数
|
||||
|
||||
代码:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
int num1 = 10;
|
||||
int num2 = 20;
|
||||
|
||||
swap(num1, num2);
|
||||
|
||||
System.out.println("num1 = " + num1);
|
||||
System.out.println("num2 = " + num2);
|
||||
}
|
||||
@ -25,13 +60,12 @@ public static void swap(int a, int b) {
|
||||
int temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
|
||||
System.out.println("a = " + a);
|
||||
System.out.println("b = " + b);
|
||||
}
|
||||
```
|
||||
|
||||
**结果:**
|
||||
输出:
|
||||
|
||||
```
|
||||
a = 20
|
||||
@ -40,103 +74,101 @@ num1 = 10
|
||||
num2 = 20
|
||||
```
|
||||
|
||||
**解析:**
|
||||
解析:
|
||||
|
||||

|
||||
在 `swap()` 方法中,`a`、`b` 的值进行交换,并不会影响到 `num1`、`num2`。因为,`a`、`b` 的值,只是从 `num1`、`num2` 的复制过来的。也就是说,a、b 相当于 `num1`、`num2` 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
|
||||
|
||||
在 swap 方法中,a、b 的值进行交换,并不会影响到 num1、num2。因为,a、b 中的值,只是从 num1、num2 的复制过来的。也就是说,a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
|
||||

|
||||
|
||||
**通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.**
|
||||
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看案例2。
|
||||
|
||||
> **example 2**
|
||||
### 案例2:传递引用类型参数1
|
||||
|
||||
代码:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
int[] arr = { 1, 2, 3, 4, 5 };
|
||||
System.out.println(arr[0]);
|
||||
change(arr);
|
||||
System.out.println(arr[0]);
|
||||
int[] arr = { 1, 2, 3, 4, 5 };
|
||||
System.out.println(arr[0]);
|
||||
change(arr);
|
||||
System.out.println(arr[0]);
|
||||
}
|
||||
|
||||
public static void change(int[] array) {
|
||||
// 将数组的第一个元素变为0
|
||||
array[0] = 0;
|
||||
// 将数组的第一个元素变为0
|
||||
array[0] = 0;
|
||||
}
|
||||
```
|
||||
|
||||
**结果:**
|
||||
输出:
|
||||
|
||||
```
|
||||
1
|
||||
0
|
||||
```
|
||||
|
||||
**解析:**
|
||||
解析:
|
||||
|
||||

|
||||

|
||||
|
||||
array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。
|
||||
看了这个案例很多人肯定觉得 Java 对引用类型的参数采用的是引用传递。
|
||||
|
||||
**通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。**
|
||||
实际上,并不是的,这里传递的还是值,不过,这个值是实参的地址罢了!
|
||||
|
||||
**很多程序设计语言(特别是,C++和 Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。**
|
||||
也就是说 `change` 方法的参数拷贝的是 `arr` (实参)的地址,因此,它和 `arr` 指向的是同一个数组对象。这也就说明了为什么方法内部对形参的修改会影响到实参。
|
||||
|
||||
> **example 3**
|
||||
为了更强有力地反驳 Java 对引用类型的参数采用的不是引用传递,我们再来看下面这个案例!
|
||||
|
||||
### 案例3 :传递引用类型参数2
|
||||
|
||||
```java
|
||||
public class Test {
|
||||
public class Person {
|
||||
private String name;
|
||||
// 省略构造函数、Getter&Setter方法
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
Student s1 = new Student("小张");
|
||||
Student s2 = new Student("小李");
|
||||
Test.swap(s1, s2);
|
||||
System.out.println("s1:" + s1.getName());
|
||||
System.out.println("s2:" + s2.getName());
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
Person xiaoZhang = new Person("小张");
|
||||
Person xiaoLi = new Person("小李");
|
||||
swap(xiaoZhang, xiaoLi);
|
||||
System.out.println("xiaoZhang:" + xiaoZhang.getName());
|
||||
System.out.println("xiaoLi:" + xiaoLi.getName());
|
||||
}
|
||||
|
||||
public static void swap(Student x, Student y) {
|
||||
Student temp = x;
|
||||
x = y;
|
||||
y = temp;
|
||||
System.out.println("x:" + x.getName());
|
||||
System.out.println("y:" + y.getName());
|
||||
}
|
||||
public static void swap(Person person1, Person person2) {
|
||||
Person temp = person1;
|
||||
person1 = person2;
|
||||
person2 = temp;
|
||||
System.out.println("person1:" + person1.getName());
|
||||
System.out.println("person2:" + person2.getName());
|
||||
}
|
||||
```
|
||||
|
||||
**结果:**
|
||||
输出:
|
||||
|
||||
```
|
||||
x:小李
|
||||
y:小张
|
||||
s1:小张
|
||||
s2:小李
|
||||
person1:小李
|
||||
person2:小张
|
||||
xiaoZhang:小张
|
||||
xiaoLi:小李
|
||||
```
|
||||
|
||||
**解析:**
|
||||
解析:
|
||||
|
||||
交换之前:
|
||||
怎么回事???两个引用类型的形参互换并没有影响实参啊!
|
||||
|
||||

|
||||
`swap` 方法的参数 `person1` 和 `person2` 只是拷贝的实参 `xiaoZhang` 和 `xiaoLi` 的地址。因此, `person1` 和 `person2` 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 `xiaoZhang` 和 `xiaoLi` 。
|
||||
|
||||
交换之后:
|
||||

|
||||
|
||||

|
||||
## 总结
|
||||
|
||||
通过上面两张图可以很清晰的看出: **方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝**
|
||||
Java 中将实参传递给方法(或函数)的方式是 **值传递** :
|
||||
|
||||
> **总结**
|
||||
- 如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
|
||||
- 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
|
||||
|
||||
Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
|
||||
值传递的。
|
||||
## 参考
|
||||
|
||||
下面再总结一下 Java 中方法参数的使用情况:
|
||||
|
||||
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
|
||||
- 一个方法可以改变一个对象参数的状态。
|
||||
- 一个方法不能让对象参数引用一个新的对象。
|
||||
|
||||
**参考:**
|
||||
|
||||
《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
|
||||
- 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
|
||||
- [Java 到底是值传递还是引用传递? - Hollis的回答 - 知乎](https://www.zhihu.com/question/31203609/answer/576030121)
|
Loading…
x
Reference in New Issue
Block a user